The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .github
    ├── FUNDING.yml
    ├── ISSUE_TEMPLATE
    │   ├── bug_report.md
    │   ├── feature-request---enhancement.md
    │   └── question.md
    ├── PULL_REQUEST_TEMPLATE.md
    └── workflows
    │   ├── tests.yaml
    │   └── website.yml
├── .gitignore
├── .golangci.yaml
├── .richstyle.yaml
├── LICENSE
├── README.md
├── auth
    ├── auth.go
    ├── jwt
    │   ├── jwt.go
    │   └── token
    │   │   ├── jwt.go
    │   │   ├── jwt_test.go
    │   │   ├── options.go
    │   │   ├── test
    │   │       ├── sample_key
    │   │       ├── sample_key 2
    │   │       └── sample_key.pub
    │   │   └── token.go
    ├── noop.go
    ├── options.go
    ├── rules.go
    └── rules_test.go
├── broker
    ├── broker.go
    ├── http.go
    ├── http_test.go
    ├── memory.go
    ├── memory_test.go
    ├── nats
    │   ├── context.go
    │   ├── nats.go
    │   ├── nats_test.go
    │   └── options.go
    ├── options.go
    └── rabbitmq
    │   ├── auth.go
    │   ├── channel.go
    │   ├── connection.go
    │   ├── connection_test.go
    │   ├── context.go
    │   ├── options.go
    │   ├── rabbitmq.go
    │   └── rabbitmq_test.go
├── cache
    ├── cache.go
    ├── memory.go
    ├── memory_test.go
    ├── options.go
    ├── options_test.go
    └── redis
    │   ├── options.go
    │   ├── options_test.go
    │   ├── redis.go
    │   └── redis_test.go
├── client
    ├── backoff.go
    ├── backoff_test.go
    ├── cache.go
    ├── cache_test.go
    ├── client.go
    ├── common_test.go
    ├── context.go
    ├── grpc
    │   ├── codec.go
    │   ├── error.go
    │   ├── grpc.go
    │   ├── grpc_pool.go
    │   ├── grpc_pool_test.go
    │   ├── grpc_test.go
    │   ├── message.go
    │   ├── options.go
    │   ├── request.go
    │   ├── request_test.go
    │   ├── response.go
    │   └── stream.go
    ├── options.go
    ├── options_test.go
    ├── retry.go
    ├── rpc_client.go
    ├── rpc_client_test.go
    ├── rpc_codec.go
    ├── rpc_message.go
    ├── rpc_request.go
    ├── rpc_request_test.go
    ├── rpc_response.go
    ├── rpc_stream.go
    └── wrapper.go
├── cmd
    ├── cmd.go
    ├── micro
    │   ├── README.md
    │   ├── cli
    │   │   ├── README.md
    │   │   ├── cli.go
    │   │   ├── new
    │   │   │   ├── new.go
    │   │   │   └── template
    │   │   │   │   ├── handler.go
    │   │   │   │   ├── ignore.go
    │   │   │   │   ├── main.go
    │   │   │   │   ├── makefile.go
    │   │   │   │   ├── module.go
    │   │   │   │   ├── proto.go
    │   │   │   │   └── readme.go
    │   │   └── util
    │   │   │   ├── dynamic.go
    │   │   │   ├── dynamic_test.go
    │   │   │   └── util.go
    │   ├── main.go
    │   ├── run
    │   │   └── run.go
    │   ├── server
    │   │   ├── server.go
    │   │   └── util_jwt.go
    │   └── web
    │   │   ├── main.js
    │   │   ├── styles.css
    │   │   └── templates
    │   │       ├── api.html
    │   │       ├── auth_login.html
    │   │       ├── auth_tokens.html
    │   │       ├── auth_users.html
    │   │       ├── base.html
    │   │       ├── form.html
    │   │       ├── home.html
    │   │       ├── log.html
    │   │       ├── logs.html
    │   │       ├── service.html
    │   │       └── status.html
    ├── options.go
    └── protoc-gen-micro
    │   ├── README.md
    │   ├── examples
    │       └── greeter
    │       │   ├── greeter.pb.go
    │       │   ├── greeter.pb.micro.go
    │       │   └── greeter.proto
    │   ├── generator
    │       ├── Makefile
    │       ├── generator.go
    │       └── name_test.go
    │   ├── main.go
    │   └── plugin
    │       └── micro
    │           └── micro.go
├── codec
    ├── bytes
    │   ├── bytes.go
    │   └── marshaler.go
    ├── codec.go
    ├── grpc
    │   ├── grpc.go
    │   └── util.go
    ├── json
    │   ├── json.go
    │   └── marshaler.go
    ├── jsonrpc
    │   ├── client.go
    │   ├── jsonrpc.go
    │   └── server.go
    ├── proto
    │   ├── marshaler.go
    │   ├── message.go
    │   └── proto.go
    ├── protorpc
    │   ├── envelope.pb.go
    │   ├── envelope.pb.micro.go
    │   ├── envelope.proto
    │   ├── netstring.go
    │   └── protorpc.go
    └── text
    │   └── text.go
├── config
    ├── README.md
    ├── config.go
    ├── default.go
    ├── default_test.go
    ├── encoder
    │   ├── encoder.go
    │   └── json
    │   │   └── json.go
    ├── loader
    │   ├── loader.go
    │   └── memory
    │   │   ├── memory.go
    │   │   └── options.go
    ├── options.go
    ├── reader
    │   ├── json
    │   │   ├── json.go
    │   │   ├── json_test.go
    │   │   ├── values.go
    │   │   └── values_test.go
    │   ├── options.go
    │   ├── preprocessor.go
    │   ├── preprocessor_test.go
    │   └── reader.go
    ├── secrets
    │   ├── box
    │   │   ├── box.go
    │   │   └── box_test.go
    │   ├── secretbox
    │   │   ├── secretbox.go
    │   │   └── secretbox_test.go
    │   └── secrets.go
    ├── source
    │   ├── changeset.go
    │   ├── cli
    │   │   ├── README.md
    │   │   ├── cli.go
    │   │   ├── cli_test.go
    │   │   ├── options.go
    │   │   └── util.go
    │   ├── env
    │   │   ├── README.md
    │   │   ├── env.go
    │   │   ├── env_test.go
    │   │   ├── options.go
    │   │   └── watcher.go
    │   ├── file
    │   │   ├── README.md
    │   │   ├── file.go
    │   │   ├── file_test.go
    │   │   ├── format.go
    │   │   ├── format_test.go
    │   │   ├── options.go
    │   │   ├── watcher.go
    │   │   ├── watcher_linux.go
    │   │   └── watcher_test.go
    │   ├── flag
    │   │   ├── README.md
    │   │   ├── flag.go
    │   │   ├── flag_test.go
    │   │   └── options.go
    │   ├── memory
    │   │   ├── README.md
    │   │   ├── memory.go
    │   │   ├── options.go
    │   │   └── watcher.go
    │   ├── nats
    │   │   ├── README.md
    │   │   ├── nats.go
    │   │   ├── options.go
    │   │   └── watcher.go
    │   ├── noop.go
    │   ├── options.go
    │   └── source.go
    └── value.go
├── debug
    ├── handler
    │   └── debug.go
    ├── log
    │   ├── log.go
    │   ├── memory
    │   │   ├── memory.go
    │   │   ├── memory_test.go
    │   │   └── stream.go
    │   ├── noop
    │   │   └── noop.go
    │   ├── options.go
    │   └── os.go
    ├── profile
    │   ├── http
    │   │   └── http.go
    │   ├── pprof
    │   │   └── pprof.go
    │   └── profile.go
    ├── proto
    │   ├── debug.pb.go
    │   ├── debug.pb.micro.go
    │   └── debug.proto
    ├── stats
    │   ├── default.go
    │   └── stats.go
    └── trace
    │   ├── default.go
    │   ├── noop.go
    │   ├── options.go
    │   └── trace.go
├── errors
    ├── errors.go
    ├── errors.pb.go
    ├── errors.pb.micro.go
    ├── errors.proto
    └── errors_test.go
├── event.go
├── events
    ├── events.go
    ├── memory.go
    ├── natsjs
    │   ├── README.md
    │   ├── helpers_test.go
    │   ├── nats.go
    │   ├── nats_test.go
    │   └── options.go
    ├── options.go
    ├── store.go
    ├── store_test.go
    └── stream_test.go
├── genai
    ├── default.go
    ├── gemini
    │   └── gemini.go
    ├── genai.go
    ├── noop.go
    ├── openai
    │   ├── openai.go
    │   └── openai_test.go
    └── options.go
├── go.mod
├── go.sum
├── internal
    ├── README.md
    └── website
    │   ├── .gitignore
    │   ├── Gemfile
    │   ├── Gemfile.lock
    │   ├── README.md
    │   ├── _config.yml
    │   ├── _layouts
    │       └── default.html
    │   ├── docs
    │       ├── architecture.md
    │       ├── broker.md
    │       ├── client-server.md
    │       ├── getting-started.md
    │       ├── index.md
    │       ├── registry.md
    │       ├── store.md
    │       └── transport.md
    │   ├── images
    │       └── logo.png
    │   └── index.html
├── logger
    ├── context.go
    ├── default.go
    ├── helper.go
    ├── level.go
    ├── logger.go
    ├── logger_test.go
    └── options.go
├── logo.png
├── metadata
    ├── metadata.go
    └── metadata_test.go
├── micro.go
├── options.go
├── profile
    └── profile.go
├── registry
    ├── cache
    │   ├── README.md
    │   ├── cache.go
    │   └── options.go
    ├── consul
    │   ├── consul.go
    │   ├── encoding.go
    │   ├── encoding_test.go
    │   ├── options.go
    │   ├── registry_test.go
    │   ├── watcher.go
    │   └── watcher_test.go
    ├── etcd
    │   ├── etcd.go
    │   ├── options.go
    │   └── watcher.go
    ├── mdns_registry.go
    ├── mdns_test.go
    ├── memory.go
    ├── memory_test.go
    ├── memory_util.go
    ├── memory_watcher.go
    ├── nats
    │   ├── nats.go
    │   ├── nats_assert_test.go
    │   ├── nats_environment_test.go
    │   ├── nats_options.go
    │   ├── nats_registry.go
    │   ├── nats_test.go
    │   ├── nats_util.go
    │   └── nats_watcher.go
    ├── options.go
    ├── options_test.go
    ├── registry.go
    └── watcher.go
├── selector
    ├── common_test.go
    ├── default.go
    ├── default_test.go
    ├── filter.go
    ├── filter_test.go
    ├── options.go
    ├── selector.go
    ├── strategy.go
    └── strategy_test.go
├── server
    ├── context.go
    ├── extractor.go
    ├── extractor_test.go
    ├── grpc
    │   ├── codec.go
    │   ├── context.go
    │   ├── error.go
    │   ├── extractor.go
    │   ├── extractor_test.go
    │   ├── grpc.go
    │   ├── handler.go
    │   ├── options.go
    │   ├── request.go
    │   ├── response.go
    │   ├── server.go
    │   ├── stream.go
    │   ├── subscriber.go
    │   └── util.go
    ├── handler.go
    ├── mock
    │   ├── mock.go
    │   ├── mock_handler.go
    │   ├── mock_subscriber.go
    │   └── mock_test.go
    ├── options.go
    ├── proto
    │   ├── server.pb.go
    │   ├── server.pb.micro.go
    │   └── server.proto
    ├── rpc_codec.go
    ├── rpc_codec_test.go
    ├── rpc_event.go
    ├── rpc_events.go
    ├── rpc_events_test.go
    ├── rpc_handler.go
    ├── rpc_helper.go
    ├── rpc_request.go
    ├── rpc_response.go
    ├── rpc_router.go
    ├── rpc_server.go
    ├── rpc_stream.go
    ├── rpc_stream_test.go
    ├── rpc_util.go
    ├── server.go
    ├── subscriber.go
    └── wrapper.go
├── service
    ├── options.go
    └── service.go
├── store
    ├── file.go
    ├── file_test.go
    ├── memory.go
    ├── mysql
    │   ├── mysql.go
    │   └── mysql_test.go
    ├── nats-js-kv
    │   ├── README.md
    │   ├── context.go
    │   ├── helpers_test.go
    │   ├── keys.go
    │   ├── nats.go
    │   ├── nats_test.go
    │   ├── options.go
    │   └── test_data.go
    ├── noop.go
    ├── options.go
    ├── postgres
    │   ├── README.md
    │   ├── metadata.go
    │   ├── pgx
    │   │   ├── README.md
    │   │   ├── db.go
    │   │   ├── metadata.go
    │   │   ├── pgx.go
    │   │   ├── pgx_test.go
    │   │   ├── queries.go
    │   │   └── templates.go
    │   ├── postgres.go
    │   └── postgres_test.go
    └── store.go
├── test
    ├── benchmark.go
    └── service.go
├── transport
    ├── context.go
    ├── grpc
    │   ├── grpc.go
    │   ├── grpc_test.go
    │   ├── handler.go
    │   ├── proto
    │   │   ├── transport.pb.go
    │   │   ├── transport.pb.micro.go
    │   │   ├── transport.proto
    │   │   └── transport_grpc.pb.go
    │   └── socket.go
    ├── headers
    │   └── headers.go
    ├── http2_buf_pool.go
    ├── http_client.go
    ├── http_client_test.go
    ├── http_listener.go
    ├── http_proxy.go
    ├── http_socket.go
    ├── http_transport.go
    ├── http_transport_test.go
    ├── memory.go
    ├── memory_test.go
    ├── nats
    │   ├── nats.go
    │   ├── nats_test.go
    │   └── options.go
    ├── options.go
    └── transport.go
├── util
    ├── addr
    │   ├── addr.go
    │   └── addr_test.go
    ├── backoff
    │   └── backoff.go
    ├── buf
    │   └── buf.go
    ├── grpc
    │   ├── grpc.go
    │   └── grpc_test.go
    ├── http
    │   ├── http.go
    │   ├── http_test.go
    │   ├── options.go
    │   └── roundtripper.go
    ├── jitter
    │   └── jitter.go
    ├── mdns
    │   ├── .gitignore
    │   ├── client.go
    │   ├── dns_sd.go
    │   ├── dns_sd_test.go
    │   ├── server.go
    │   ├── server_test.go
    │   ├── zone.go
    │   └── zone_test.go
    ├── net
    │   ├── net.go
    │   └── net_test.go
    ├── pool
    │   ├── default.go
    │   ├── default_test.go
    │   ├── options.go
    │   └── pool.go
    ├── registry
    │   ├── util.go
    │   └── util_test.go
    ├── ring
    │   ├── buffer.go
    │   └── buffer_test.go
    ├── signal
    │   └── signal.go
    ├── socket
    │   ├── pool.go
    │   └── socket.go
    ├── test
    │   └── test.go
    ├── tls
    │   └── tls.go
    └── wrapper
    │   ├── wrapper.go
    │   └── wrapper_test.go
├── web
    ├── options.go
    ├── service.go
    ├── service_test.go
    ├── web.go
    └── web_test.go
└── wrapper
    └── trace
        └── opentelemetry
            ├── README.md
            ├── opentelemetry.go
            ├── options.go
            └── wrapper.go


/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: asim
2 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Bug report
 3 | about: For reporting bugs in go-micro
 4 | title: "[BUG]"
 5 | labels: ""
 6 | assignees: ""
 7 | ---
 8 | 
 9 | ## Describe the bug
10 | 
11 | 1. What are you trying to do?
12 | 2. What did you expect to happen?
13 | 3. What happens instead?
14 | 
15 | ## How to reproduce the bug
16 | 
17 | If possible, please include a minimal code snippet here.
18 | 
19 | ## Environment
20 | 
21 | Go Version: please paste `go version` output here
22 | 
23 | ```go
24 | please paste `go env` output here
25 | ```
26 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request---enhancement.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Feature request / Enhancement
 3 | about: If you have a need not served by go-micro
 4 | title: "[FEATURE]"
 5 | labels: ""
 6 | assignees: ""
 7 | ---
 8 | 
 9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 | 
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 | 
15 | **Additional context**
16 | Add any other context or screenshots about the feature request here.
17 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: Question
 3 | about: Ask a question about go-micro
 4 | title: ""
 5 | labels: ""
 6 | assignees: ""
 7 | ---
 8 | 
 9 | Before asking, please check if your question has already been answered:
10 | 
11 | 1. Check the documentation - https://micro.mu/docs/
12 | 2. Check the examples and plugins - https://github.com/micro/examples & https://github.com/micro/go-plugins
13 | 3. Search existing issues
14 | 


--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
 1 | # Pull Request template
 2 | 
 3 | Please, go through these steps before clicking submit on this PR.
 4 | 
 5 | 1. Make sure this PR targets the `develop` branch. We follow the git-flow branching model.
 6 | 2. Give a descriptive title to your PR.
 7 | 3. Provide a description of your changes.
 8 | 4. Make sure you have some relevant tests.
 9 | 5. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).
10 | 
11 | ## PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING
12 | 


--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
 1 | name: Run Tests
 2 | on:
 3 |   push:
 4 |     branches:
 5 |       - "**"
 6 |   pull_request:
 7 |     types:
 8 |       - opened
 9 |       - reopened
10 |     branches:
11 |       - "**"
12 | jobs:
13 |   unittests:
14 |     name: Unit Tests
15 |     runs-on: ubuntu-latest
16 |     steps:
17 |       - uses: actions/checkout@v3
18 |       - name: Set up Go
19 |         uses: actions/setup-go@v3
20 |         with:
21 |           go-version: 1.24
22 |           check-latest: true
23 |           cache: true
24 |       - name: Get dependencies
25 |         run: |
26 |           go install github.com/kyoh86/richgo@latest
27 |           go get -v -t -d ./...
28 |       - name: Run tests
29 |         id: tests
30 |         run: richgo test -v -race -cover ./...
31 |         env:
32 |           IN_TRAVIS_CI: yes
33 |           RICHGO_FORCE_COLOR: 1
34 | 


--------------------------------------------------------------------------------
/.github/workflows/website.yml:
--------------------------------------------------------------------------------
 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages
 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled
 3 | 
 4 | on:
 5 |   # Runs on pushes targeting the default branch
 6 |   push:
 7 |     branches: ["master"]
 8 | 
 9 |   # Allows you to run this workflow manually from the Actions tab
10 |   workflow_dispatch:
11 | 
12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
13 | permissions:
14 |   contents: read
15 |   pages: write
16 |   id-token: write
17 | 
18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
20 | concurrency:
21 |   group: "pages"
22 |   cancel-in-progress: false
23 | 
24 | jobs:
25 |   # Build job
26 |   build:
27 |     runs-on: ubuntu-latest
28 |     steps:
29 |       - name: Checkout
30 |         uses: actions/checkout@v4
31 |       - name: Setup Pages
32 |         uses: actions/configure-pages@v5
33 |       - name: Build with Jekyll
34 |         uses: actions/jekyll-build-pages@v1
35 |         with:
36 |           source: ./internal/website
37 |           destination: ./_site
38 |       - name: Upload artifact
39 |         uses: actions/upload-pages-artifact@v3
40 | 
41 |   # Deployment job
42 |   deploy:
43 |     environment:
44 |       name: github-pages
45 |       url: ${{ steps.deployment.outputs.page_url }}
46 |     runs-on: ubuntu-latest
47 |     needs: build
48 |     steps:
49 |       - name: Deploy to GitHub Pages
50 |         id: deployment
51 |         uses: actions/deploy-pages@v4
52 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | # Develop tools
 2 | /.vscode/
 3 | /.idea/
 4 | /.trunk
 5 | 
 6 | # Binaries for programs and plugins
 7 | *.exe
 8 | *.exe~
 9 | *.dll
10 | *.so
11 | *.dylib
12 | 
13 | # Folders
14 | _obj
15 | _test
16 | _build
17 | 
18 | # Architecture specific extensions/prefixes
19 | *.[568vq]
20 | [568vq].out
21 | 
22 | *.cgo1.go
23 | *.cgo2.c
24 | _cgo_defun.c
25 | _cgo_gotypes.go
26 | _cgo_export.*
27 | 
28 | # Test binary, build with `go test -c`
29 | *.test
30 | 
31 | # Output of the go coverage tool, specifically when used with LiteIDE
32 | *.out
33 | 
34 | # vim temp files
35 | *~
36 | *.swp
37 | *.swo
38 | 
39 | # go work files
40 | go.work
41 | go.work.sum
42 | 


--------------------------------------------------------------------------------
/.richstyle.yaml:
--------------------------------------------------------------------------------
 1 | labelType: long
 2 | coverThreshold: 70
 3 | buildStyle:
 4 |   bold: true
 5 |   foreground: yellow
 6 | startStyle:
 7 |   foreground: lightBlack
 8 | passStyle:
 9 |   foreground: green
10 | failStyle:
11 |   bold: true
12 |   foreground: "#821515"
13 | skipStyle:
14 |   foreground: lightBlack
15 | passPackageStyle:
16 |   foreground: green
17 |   hide: false
18 | failPackageStyle:
19 |   bold: true
20 |   foreground: "#821515"
21 | coveredStyle:
22 |   foreground: green
23 | uncoveredStyle:
24 |   bold: true
25 |   foreground: yellow
26 | fileStyle:
27 |   foreground: cyan
28 | lineStyle:
29 |   foreground: magenta
30 | 


--------------------------------------------------------------------------------
/auth/jwt/token/test/sample_key.pub:
--------------------------------------------------------------------------------
1 | LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE4U2JKUDVYYkVpZFJtNWIyc05wTApHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkL1JsOS8xcE1WN01pTzNMSHd0aEhDMkJSWXFyKzF3RmRvWkNHCkJZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUwQkgvbFhRTXF5RXFGNU1JMnplakM0ek16cjE1T04rZ0U0Sm4KaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtL21VZElUL0xhRjdrUXh5WUs1VktuK05nT1d6TWx6S0FBcENuNwpUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsbzlqRGpsWTVvQk9jemZxZU5XSEs1R1hCN1F3cExOaDk0NlB6ClpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDV3bEVxOGhOaG1obTVOTmUvTytHZ2pCRE5TZlVoMDYrcTRuZ20KYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1b1J0UWhnaVk5MTBwUGY5YmF1WFdxd1VDVWE0cXNIempLUjBMLwpOMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVcmdMclBiRUI5ZWdjR2szOCticEtzM1o2UnI1K3RuRDFCSVBJCkZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVVUR0R4VXg4b2o4VkllUm5XRHE2TWMxaUpwOFV5Y2lCSVRSdHcKNGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMGxhUXpBdVAzYWlXWElNcDJzYzhTYytCbCtMalhtQm94QnJhQgpIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YZ0ZLU3NJbkhEckhWT3lXUEJlM2ZhZEVjNzdiK25iL2V4T3pyCjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=


--------------------------------------------------------------------------------
/auth/jwt/token/token.go:
--------------------------------------------------------------------------------
 1 | package token
 2 | 
 3 | import (
 4 | 	"errors"
 5 | 	"time"
 6 | 
 7 | 	"go-micro.dev/v5/auth"
 8 | )
 9 | 
10 | var (
11 | 	// ErrNotFound is returned when a token cannot be found.
12 | 	ErrNotFound = errors.New("token not found")
13 | 	// ErrEncodingToken is returned when the service encounters an error during encoding.
14 | 	ErrEncodingToken = errors.New("error encoding the token")
15 | 	// ErrInvalidToken is returned when the token provided is not valid.
16 | 	ErrInvalidToken = errors.New("invalid token provided")
17 | )
18 | 
19 | // Provider generates and inspects tokens.
20 | type Provider interface {
21 | 	Generate(account *auth.Account, opts ...GenerateOption) (*Token, error)
22 | 	Inspect(token string) (*auth.Account, error)
23 | 	String() string
24 | }
25 | 
26 | type Token struct {
27 | 	// The actual token
28 | 	Token string `json:"token"`
29 | 	// Time of token creation
30 | 	Created time.Time `json:"created"`
31 | 	// Time of token expiry
32 | 	Expiry time.Time `json:"expiry"`
33 | }
34 | 


--------------------------------------------------------------------------------
/broker/memory_test.go:
--------------------------------------------------------------------------------
 1 | package broker_test
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"testing"
 6 | 
 7 | 	"go-micro.dev/v5/broker"
 8 | )
 9 | 
10 | func TestMemoryBroker(t *testing.T) {
11 | 	b := broker.NewMemoryBroker()
12 | 
13 | 	if err := b.Connect(); err != nil {
14 | 		t.Fatalf("Unexpected connect error %v", err)
15 | 	}
16 | 
17 | 	topic := "test"
18 | 	count := 10
19 | 
20 | 	fn := func(p broker.Event) error {
21 | 		return nil
22 | 	}
23 | 
24 | 	sub, err := b.Subscribe(topic, fn)
25 | 	if err != nil {
26 | 		t.Fatalf("Unexpected error subscribing %v", err)
27 | 	}
28 | 
29 | 	for i := 0; i < count; i++ {
30 | 		message := &broker.Message{
31 | 			Header: map[string]string{
32 | 				"foo": "bar",
33 | 				"id":  fmt.Sprintf("%d", i),
34 | 			},
35 | 			Body: []byte(`hello world`),
36 | 		}
37 | 
38 | 		if err := b.Publish(topic, message); err != nil {
39 | 			t.Fatalf("Unexpected error publishing %d", i)
40 | 		}
41 | 	}
42 | 
43 | 	if err := sub.Unsubscribe(); err != nil {
44 | 		t.Fatalf("Unexpected error unsubscribing from %s: %v", topic, err)
45 | 	}
46 | 
47 | 	if err := b.Disconnect(); err != nil {
48 | 		t.Fatalf("Unexpected connect error %v", err)
49 | 	}
50 | }
51 | 


--------------------------------------------------------------------------------
/broker/nats/context.go:
--------------------------------------------------------------------------------
 1 | package nats
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/broker"
 7 | )
 8 | 
 9 | // setBrokerOption returns a function to setup a context with given value.
10 | func setBrokerOption(k, v interface{}) broker.Option {
11 | 	return func(o *broker.Options) {
12 | 		if o.Context == nil {
13 | 			o.Context = context.Background()
14 | 		}
15 | 		o.Context = context.WithValue(o.Context, k, v)
16 | 	}
17 | }
18 | 


--------------------------------------------------------------------------------
/broker/nats/options.go:
--------------------------------------------------------------------------------
 1 | package nats
 2 | 
 3 | import (
 4 | 	natsp "github.com/nats-io/nats.go"
 5 | 	"go-micro.dev/v5/broker"
 6 | )
 7 | 
 8 | type optionsKey struct{}
 9 | type drainConnectionKey struct{}
10 | 
11 | // Options accepts nats.Options.
12 | func Options(opts natsp.Options) broker.Option {
13 | 	return setBrokerOption(optionsKey{}, opts)
14 | }
15 | 
16 | // DrainConnection will drain subscription on close.
17 | func DrainConnection() broker.Option {
18 | 	return setBrokerOption(drainConnectionKey{}, struct{}{})
19 | }
20 | 


--------------------------------------------------------------------------------
/broker/rabbitmq/auth.go:
--------------------------------------------------------------------------------
 1 | package rabbitmq
 2 | 
 3 | type ExternalAuthentication struct {
 4 | }
 5 | 
 6 | func (auth *ExternalAuthentication) Mechanism() string {
 7 | 	return "EXTERNAL"
 8 | }
 9 | 
10 | func (auth *ExternalAuthentication) Response() string {
11 | 	return ""
12 | }
13 | 


--------------------------------------------------------------------------------
/broker/rabbitmq/context.go:
--------------------------------------------------------------------------------
 1 | package rabbitmq
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/broker"
 7 | 	"go-micro.dev/v5/server"
 8 | )
 9 | 
10 | // setSubscribeOption returns a function to setup a context with given value.
11 | func setSubscribeOption(k, v interface{}) broker.SubscribeOption {
12 | 	return func(o *broker.SubscribeOptions) {
13 | 		if o.Context == nil {
14 | 			o.Context = context.Background()
15 | 		}
16 | 		o.Context = context.WithValue(o.Context, k, v)
17 | 	}
18 | }
19 | 
20 | // setBrokerOption returns a function to setup a context with given value.
21 | func setBrokerOption(k, v interface{}) broker.Option {
22 | 	return func(o *broker.Options) {
23 | 		if o.Context == nil {
24 | 			o.Context = context.Background()
25 | 		}
26 | 		o.Context = context.WithValue(o.Context, k, v)
27 | 	}
28 | }
29 | 
30 | // setBrokerOption returns a function to setup a context with given value.
31 | func setServerSubscriberOption(k, v interface{}) server.SubscriberOption {
32 | 	return func(o *server.SubscriberOptions) {
33 | 		if o.Context == nil {
34 | 			o.Context = context.Background()
35 | 		}
36 | 		o.Context = context.WithValue(o.Context, k, v)
37 | 	}
38 | }
39 | 
40 | // setPublishOption returns a function to setup a context with given value.
41 | func setPublishOption(k, v interface{}) broker.PublishOption {
42 | 	return func(o *broker.PublishOptions) {
43 | 		if o.Context == nil {
44 | 			o.Context = context.Background()
45 | 		}
46 | 		o.Context = context.WithValue(o.Context, k, v)
47 | 	}
48 | }
49 | 


--------------------------------------------------------------------------------
/cache/memory.go:
--------------------------------------------------------------------------------
 1 | package cache
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"sync"
 6 | 	"time"
 7 | )
 8 | 
 9 | type memCache struct {
10 | 	opts Options
11 | 
12 | 	items map[string]Item
13 | 	sync.RWMutex
14 | }
15 | 
16 | func (c *memCache) Get(ctx context.Context, key string) (interface{}, time.Time, error) {
17 | 	c.RWMutex.RLock()
18 | 	defer c.RWMutex.RUnlock()
19 | 
20 | 	item, found := c.items[key]
21 | 	if !found {
22 | 		return nil, time.Time{}, ErrKeyNotFound
23 | 	}
24 | 	if item.Expired() {
25 | 		return nil, time.Time{}, ErrItemExpired
26 | 	}
27 | 
28 | 	return item.Value, time.Unix(0, item.Expiration), nil
29 | }
30 | 
31 | func (c *memCache) Put(ctx context.Context, key string, val interface{}, d time.Duration) error {
32 | 	var e int64
33 | 	if d == DefaultExpiration {
34 | 		d = c.opts.Expiration
35 | 	}
36 | 	if d > 0 {
37 | 		e = time.Now().Add(d).UnixNano()
38 | 	}
39 | 
40 | 	c.RWMutex.Lock()
41 | 	defer c.RWMutex.Unlock()
42 | 
43 | 	c.items[key] = Item{
44 | 		Value:      val,
45 | 		Expiration: e,
46 | 	}
47 | 
48 | 	return nil
49 | }
50 | 
51 | func (c *memCache) Delete(ctx context.Context, key string) error {
52 | 	c.RWMutex.Lock()
53 | 	defer c.RWMutex.Unlock()
54 | 
55 | 	_, found := c.items[key]
56 | 	if !found {
57 | 		return ErrKeyNotFound
58 | 	}
59 | 
60 | 	delete(c.items, key)
61 | 	return nil
62 | }
63 | 
64 | func (m *memCache) String() string {
65 | 	return "memory"
66 | }
67 | 


--------------------------------------------------------------------------------
/cache/options_test.go:
--------------------------------------------------------------------------------
 1 | package cache
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 	"time"
 6 | )
 7 | 
 8 | func TestOptions(t *testing.T) {
 9 | 	testData := map[string]struct {
10 | 		set        bool
11 | 		expiration time.Duration
12 | 		items      map[string]Item
13 | 	}{
14 | 		"DefaultOptions":  {false, DefaultExpiration, map[string]Item{}},
15 | 		"ModifiedOptions": {true, time.Second, map[string]Item{"test": {"hello go-micro", 0}}},
16 | 	}
17 | 
18 | 	for k, d := range testData {
19 | 		t.Run(k, func(t *testing.T) {
20 | 			var opts Options
21 | 
22 | 			if d.set {
23 | 				opts = NewOptions(
24 | 					Expiration(d.expiration),
25 | 					Items(d.items),
26 | 				)
27 | 			} else {
28 | 				opts = NewOptions()
29 | 			}
30 | 
31 | 			// test options
32 | 			for _, o := range []Options{opts} {
33 | 				if o.Expiration != d.expiration {
34 | 					t.Fatalf("Expected expiration '%v', got '%v'", d.expiration, o.Expiration)
35 | 				}
36 | 
37 | 				if o.Items["test"] != d.items["test"] {
38 | 					t.Fatalf("Expected items %#v, got %#v", d.items, o.Items)
39 | 				}
40 | 			}
41 | 		})
42 | 	}
43 | }
44 | 


--------------------------------------------------------------------------------
/cache/redis/options.go:
--------------------------------------------------------------------------------
 1 | package redis
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	rclient "github.com/go-redis/redis/v8"
 7 | 	"go-micro.dev/v5/cache"
 8 | )
 9 | 
10 | type redisOptionsContextKey struct{}
11 | 
12 | // WithRedisOptions sets advanced options for redis.
13 | func WithRedisOptions(options rclient.UniversalOptions) cache.Option {
14 | 	return func(o *cache.Options) {
15 | 		if o.Context == nil {
16 | 			o.Context = context.Background()
17 | 		}
18 | 
19 | 		o.Context = context.WithValue(o.Context, redisOptionsContextKey{}, options)
20 | 	}
21 | }
22 | 
23 | func newUniversalClient(options cache.Options) rclient.UniversalClient {
24 | 	if options.Context == nil {
25 | 		options.Context = context.Background()
26 | 	}
27 | 
28 | 	opts, ok := options.Context.Value(redisOptionsContextKey{}).(rclient.UniversalOptions)
29 | 	if !ok {
30 | 		addr := "redis://127.0.0.1:6379"
31 | 		if len(options.Address) > 0 {
32 | 			addr = options.Address
33 | 		}
34 | 
35 | 		redisOptions, err := rclient.ParseURL(addr)
36 | 		if err != nil {
37 | 			redisOptions = &rclient.Options{Addr: addr}
38 | 		}
39 | 
40 | 		return rclient.NewClient(redisOptions)
41 | 	}
42 | 
43 | 	if len(opts.Addrs) == 0 && len(options.Address) > 0 {
44 | 		opts.Addrs = []string{options.Address}
45 | 	}
46 | 
47 | 	return rclient.NewUniversalClient(&opts)
48 | }
49 | 


--------------------------------------------------------------------------------
/cache/redis/redis.go:
--------------------------------------------------------------------------------
 1 | package redis
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"time"
 6 | 
 7 | 	rclient "github.com/go-redis/redis/v8"
 8 | 	"go-micro.dev/v5/cache"
 9 | )
10 | 
11 | // NewRedisCache returns a new redis cache.
12 | func NewRedisCache(opts ...cache.Option) cache.Cache {
13 | 	options := cache.NewOptions(opts...)
14 | 	return &redisCache{
15 | 		opts:   options,
16 | 		client: newUniversalClient(options),
17 | 	}
18 | }
19 | 
20 | type redisCache struct {
21 | 	opts   cache.Options
22 | 	client rclient.UniversalClient
23 | }
24 | 
25 | func (c *redisCache) Get(ctx context.Context, key string) (interface{}, time.Time, error) {
26 | 	val, err := c.client.Get(ctx, key).Bytes()
27 | 	if err != nil && err == rclient.Nil {
28 | 		return nil, time.Time{}, cache.ErrKeyNotFound
29 | 	} else if err != nil {
30 | 		return nil, time.Time{}, err
31 | 	}
32 | 
33 | 	dur, err := c.client.TTL(ctx, key).Result()
34 | 	if err != nil {
35 | 		return nil, time.Time{}, err
36 | 	}
37 | 	if dur == -1 {
38 | 		return val, time.Unix(1<<63-1, 0), nil
39 | 	}
40 | 	if dur == -2 {
41 | 		return val, time.Time{}, cache.ErrItemExpired
42 | 	}
43 | 
44 | 	return val, time.Now().Add(dur), nil
45 | }
46 | 
47 | func (c *redisCache) Put(ctx context.Context, key string, val interface{}, dur time.Duration) error {
48 | 	return c.client.Set(ctx, key, val, dur).Err()
49 | }
50 | 
51 | func (c *redisCache) Delete(ctx context.Context, key string) error {
52 | 	return c.client.Del(ctx, key).Err()
53 | }
54 | 
55 | func (m *redisCache) String() string {
56 | 	return "redis"
57 | }
58 | 


--------------------------------------------------------------------------------
/client/backoff.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"time"
 6 | 
 7 | 	"go-micro.dev/v5/util/backoff"
 8 | )
 9 | 
10 | type BackoffFunc func(ctx context.Context, req Request, attempts int) (time.Duration, error)
11 | 
12 | func exponentialBackoff(ctx context.Context, req Request, attempts int) (time.Duration, error) {
13 | 	return backoff.Do(attempts), nil
14 | }
15 | 


--------------------------------------------------------------------------------
/client/backoff_test.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"testing"
 6 | 	"time"
 7 | )
 8 | 
 9 | func TestBackoff(t *testing.T) {
10 | 	results := []time.Duration{
11 | 		0 * time.Second,
12 | 		100 * time.Millisecond,
13 | 		600 * time.Millisecond,
14 | 		1900 * time.Millisecond,
15 | 		4300 * time.Millisecond,
16 | 		7900 * time.Millisecond,
17 | 	}
18 | 
19 | 	c := NewClient()
20 | 
21 | 	for i := 0; i < 5; i++ {
22 | 		d, err := exponentialBackoff(context.TODO(), c.NewRequest("test", "test", nil), i)
23 | 		if err != nil {
24 | 			t.Fatal(err)
25 | 		}
26 | 
27 | 		if d != results[i] {
28 | 			t.Fatalf("Expected equal than %v, got %v", results[i], d)
29 | 		}
30 | 	}
31 | }
32 | 


--------------------------------------------------------------------------------
/client/cache.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"encoding/json"
 6 | 	"fmt"
 7 | 	"hash/fnv"
 8 | 	"time"
 9 | 
10 | 	cache "github.com/patrickmn/go-cache"
11 | 
12 | 	"go-micro.dev/v5/metadata"
13 | 	"go-micro.dev/v5/transport/headers"
14 | )
15 | 
16 | // NewCache returns an initialized cache.
17 | func NewCache() *Cache {
18 | 	return &Cache{
19 | 		cache: cache.New(cache.NoExpiration, 30*time.Second),
20 | 	}
21 | }
22 | 
23 | // Cache for responses.
24 | type Cache struct {
25 | 	cache *cache.Cache
26 | }
27 | 
28 | // Get a response from the cache.
29 | func (c *Cache) Get(ctx context.Context, req *Request) (interface{}, bool) {
30 | 	return c.cache.Get(key(ctx, req))
31 | }
32 | 
33 | // Set a response in the cache.
34 | func (c *Cache) Set(ctx context.Context, req *Request, rsp interface{}, expiry time.Duration) {
35 | 	c.cache.Set(key(ctx, req), rsp, expiry)
36 | }
37 | 
38 | // List the key value pairs in the cache.
39 | func (c *Cache) List() map[string]string {
40 | 	items := c.cache.Items()
41 | 
42 | 	rsp := make(map[string]string, len(items))
43 | 
44 | 	for k, v := range items {
45 | 		bytes, _ := json.Marshal(v.Object)
46 | 		rsp[k] = string(bytes)
47 | 	}
48 | 
49 | 	return rsp
50 | }
51 | 
52 | // key returns a hash for the context and request.
53 | func key(ctx context.Context, req *Request) string {
54 | 	ns, _ := metadata.Get(ctx, headers.Namespace)
55 | 
56 | 	bytes, _ := json.Marshal(map[string]interface{}{
57 | 		"namespace": ns,
58 | 		"request": map[string]interface{}{
59 | 			"service":  (*req).Service(),
60 | 			"endpoint": (*req).Endpoint(),
61 | 			"method":   (*req).Method(),
62 | 			"body":     (*req).Body(),
63 | 		},
64 | 	})
65 | 
66 | 	h := fnv.New64()
67 | 	h.Write(bytes)
68 | 
69 | 	return fmt.Sprintf("%x", h.Sum(nil))
70 | }
71 | 


--------------------------------------------------------------------------------
/client/common_test.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/registry"
 5 | )
 6 | 
 7 | var (
 8 | 	// mock data.
 9 | 	testData = map[string][]*registry.Service{
10 | 		"foo": {
11 | 			{
12 | 				Name:    "foo",
13 | 				Version: "1.0.0",
14 | 				Nodes: []*registry.Node{
15 | 					{
16 | 						Id:      "foo-1.0.0-123",
17 | 						Address: "localhost:9999",
18 | 						Metadata: map[string]string{
19 | 							"protocol": "mucp",
20 | 						},
21 | 					},
22 | 					{
23 | 						Id:      "foo-1.0.0-321",
24 | 						Address: "localhost:9999",
25 | 						Metadata: map[string]string{
26 | 							"protocol": "mucp",
27 | 						},
28 | 					},
29 | 				},
30 | 			},
31 | 			{
32 | 				Name:    "foo",
33 | 				Version: "1.0.1",
34 | 				Nodes: []*registry.Node{
35 | 					{
36 | 						Id:      "foo-1.0.1-321",
37 | 						Address: "localhost:6666",
38 | 						Metadata: map[string]string{
39 | 							"protocol": "mucp",
40 | 						},
41 | 					},
42 | 				},
43 | 			},
44 | 			{
45 | 				Name:    "foo",
46 | 				Version: "1.0.3",
47 | 				Nodes: []*registry.Node{
48 | 					{
49 | 						Id:      "foo-1.0.3-345",
50 | 						Address: "localhost:8888",
51 | 						Metadata: map[string]string{
52 | 							"protocol": "mucp",
53 | 						},
54 | 					},
55 | 				},
56 | 			},
57 | 		},
58 | 	}
59 | )
60 | 


--------------------------------------------------------------------------------
/client/context.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"context"
 5 | )
 6 | 
 7 | type clientKey struct{}
 8 | 
 9 | func FromContext(ctx context.Context) (Client, bool) {
10 | 	c, ok := ctx.Value(clientKey{}).(Client)
11 | 	return c, ok
12 | }
13 | 
14 | func NewContext(ctx context.Context, c Client) context.Context {
15 | 	return context.WithValue(ctx, clientKey{}, c)
16 | }
17 | 


--------------------------------------------------------------------------------
/client/grpc/error.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"net/http"
 5 | 
 6 | 	"go-micro.dev/v5/errors"
 7 | 	"google.golang.org/grpc/codes"
 8 | 	"google.golang.org/grpc/status"
 9 | )
10 | 
11 | func microError(err error) error {
12 | 	// no error
13 | 	switch err {
14 | 	case nil:
15 | 		return nil
16 | 	}
17 | 
18 | 	if verr, ok := err.(*errors.Error); ok {
19 | 		return verr
20 | 	}
21 | 
22 | 	// grpc error
23 | 	s, ok := status.FromError(err)
24 | 	if !ok {
25 | 		return err
26 | 	}
27 | 
28 | 	// return first error from details
29 | 	if details := s.Details(); len(details) > 0 {
30 | 		return microError(details[0].(error))
31 | 	}
32 | 
33 | 	// try to decode micro *errors.Error
34 | 	if e := errors.Parse(s.Message()); e.Code > 0 {
35 | 		return e // actually a micro error
36 | 	}
37 | 
38 | 	// fallback
39 | 	return errors.New("go.micro.client", s.Message(), microStatusFromGrpcCode(s.Code()))
40 | }
41 | 
42 | func microStatusFromGrpcCode(code codes.Code) int32 {
43 | 	switch code {
44 | 	case codes.OK:
45 | 		return http.StatusOK
46 | 	case codes.InvalidArgument:
47 | 		return http.StatusBadRequest
48 | 	case codes.DeadlineExceeded:
49 | 		return http.StatusRequestTimeout
50 | 	case codes.NotFound:
51 | 		return http.StatusNotFound
52 | 	case codes.AlreadyExists:
53 | 		return http.StatusConflict
54 | 	case codes.PermissionDenied:
55 | 		return http.StatusForbidden
56 | 	case codes.Unauthenticated:
57 | 		return http.StatusUnauthorized
58 | 	case codes.FailedPrecondition:
59 | 		return http.StatusPreconditionFailed
60 | 	case codes.Unimplemented:
61 | 		return http.StatusNotImplemented
62 | 	case codes.Internal:
63 | 		return http.StatusInternalServerError
64 | 	case codes.Unavailable:
65 | 		return http.StatusServiceUnavailable
66 | 	}
67 | 
68 | 	return http.StatusInternalServerError
69 | }
70 | 


--------------------------------------------------------------------------------
/client/grpc/grpc_pool_test.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"net"
 6 | 	"testing"
 7 | 	"time"
 8 | 
 9 | 	"google.golang.org/grpc"
10 | 	pb "google.golang.org/grpc/examples/helloworld/helloworld"
11 | )
12 | 
13 | func testPool(t *testing.T, size int, ttl time.Duration, idle int, ms int) {
14 | 	// setup server
15 | 	l, err := net.Listen("tcp", ":0")
16 | 	if err != nil {
17 | 		t.Errorf("failed to listen: %v", err)
18 | 	}
19 | 	defer l.Close()
20 | 
21 | 	s := grpc.NewServer()
22 | 	pb.RegisterGreeterServer(s, &greeterServer{})
23 | 
24 | 	go s.Serve(l)
25 | 	defer s.Stop()
26 | 
27 | 	// zero pool
28 | 	p := newPool(size, ttl, idle, ms)
29 | 
30 | 	for i := 0; i < 10; i++ {
31 | 		// get a conn
32 | 		cc, err := p.getConn(context.TODO(), l.Addr().String(), grpc.WithInsecure())
33 | 		if err != nil {
34 | 			t.Fatal(err)
35 | 		}
36 | 
37 | 		rsp := pb.HelloReply{}
38 | 
39 | 		err = cc.Invoke(context.TODO(), "/helloworld.Greeter/SayHello", &pb.HelloRequest{Name: "John"}, &rsp)
40 | 		if err != nil {
41 | 			t.Fatal(err)
42 | 		}
43 | 
44 | 		if rsp.Message != "Hello John" {
45 | 			t.Errorf("Got unexpected response %v", rsp.Message)
46 | 		}
47 | 
48 | 		// release the conn
49 | 		p.release(l.Addr().String(), cc, nil)
50 | 
51 | 		p.Lock()
52 | 		if i := p.conns[l.Addr().String()].count; i > size {
53 | 			p.Unlock()
54 | 			t.Errorf("pool size %d is greater than expected %d", i, size)
55 | 		}
56 | 		p.Unlock()
57 | 	}
58 | }
59 | 
60 | func TestGRPCPool(t *testing.T) {
61 | 	testPool(t, 0, time.Minute, 10, 2)
62 | 	testPool(t, 2, time.Minute, 10, 1)
63 | }
64 | 


--------------------------------------------------------------------------------
/client/grpc/message.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/client"
 5 | )
 6 | 
 7 | type grpcEvent struct {
 8 | 	topic       string
 9 | 	contentType string
10 | 	payload     interface{}
11 | }
12 | 
13 | func newGRPCEvent(topic string, payload interface{}, contentType string, opts ...client.MessageOption) client.Message {
14 | 	var options client.MessageOptions
15 | 	for _, o := range opts {
16 | 		o(&options)
17 | 	}
18 | 
19 | 	if len(options.ContentType) > 0 {
20 | 		contentType = options.ContentType
21 | 	}
22 | 
23 | 	return &grpcEvent{
24 | 		payload:     payload,
25 | 		topic:       topic,
26 | 		contentType: contentType,
27 | 	}
28 | }
29 | 
30 | func (g *grpcEvent) ContentType() string {
31 | 	return g.contentType
32 | }
33 | 
34 | func (g *grpcEvent) Topic() string {
35 | 	return g.topic
36 | }
37 | 
38 | func (g *grpcEvent) Payload() interface{} {
39 | 	return g.payload
40 | }
41 | 


--------------------------------------------------------------------------------
/client/grpc/request_test.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"testing"
 5 | )
 6 | 
 7 | func TestMethodToGRPC(t *testing.T) {
 8 | 	testData := []struct {
 9 | 		service string
10 | 		method  string
11 | 		expect  string
12 | 	}{
13 | 		{
14 | 			"helloworld",
15 | 			"Greeter.SayHello",
16 | 			"/helloworld.Greeter/SayHello",
17 | 		},
18 | 		{
19 | 			"helloworld",
20 | 			"/helloworld.Greeter/SayHello",
21 | 			"/helloworld.Greeter/SayHello",
22 | 		},
23 | 		{
24 | 			"",
25 | 			"/helloworld.Greeter/SayHello",
26 | 			"/helloworld.Greeter/SayHello",
27 | 		},
28 | 		{
29 | 			"",
30 | 			"Greeter.SayHello",
31 | 			"/Greeter/SayHello",
32 | 		},
33 | 	}
34 | 
35 | 	for _, d := range testData {
36 | 		method := methodToGRPC(d.service, d.method)
37 | 		if method != d.expect {
38 | 			t.Fatalf("expected %s got %s", d.expect, method)
39 | 		}
40 | 	}
41 | }
42 | 


--------------------------------------------------------------------------------
/client/grpc/response.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"strings"
 5 | 
 6 | 	"go-micro.dev/v5/codec"
 7 | 	"go-micro.dev/v5/codec/bytes"
 8 | 	"google.golang.org/grpc"
 9 | 	"google.golang.org/grpc/encoding"
10 | )
11 | 
12 | type response struct {
13 | 	conn   *grpc.ClientConn
14 | 	stream grpc.ClientStream
15 | 	codec  encoding.Codec
16 | 	gcodec codec.Codec
17 | }
18 | 
19 | // Read the response.
20 | func (r *response) Codec() codec.Reader {
21 | 	return r.gcodec
22 | }
23 | 
24 | // read the header.
25 | func (r *response) Header() map[string]string {
26 | 	md, err := r.stream.Header()
27 | 	if err != nil {
28 | 		return map[string]string{}
29 | 	}
30 | 	hdr := make(map[string]string, len(md))
31 | 	for k, v := range md {
32 | 		hdr[k] = strings.Join(v, ",")
33 | 	}
34 | 	return hdr
35 | }
36 | 
37 | // Read the undecoded response.
38 | func (r *response) Read() ([]byte, error) {
39 | 	f := &bytes.Frame{}
40 | 	if err := r.gcodec.ReadBody(f); err != nil {
41 | 		return nil, err
42 | 	}
43 | 	return f.Data, nil
44 | }
45 | 


--------------------------------------------------------------------------------
/client/grpc/stream.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"io"
 6 | 	"sync"
 7 | 
 8 | 	"go-micro.dev/v5/client"
 9 | 	"google.golang.org/grpc"
10 | )
11 | 
12 | // Implements the streamer interface.
13 | type grpcStream struct {
14 | 	sync.RWMutex
15 | 	closed   bool
16 | 	err      error
17 | 	stream   grpc.ClientStream
18 | 	request  client.Request
19 | 	response client.Response
20 | 	context  context.Context
21 | 	cancel   func()
22 | 	release  func(error)
23 | }
24 | 
25 | func (g *grpcStream) Context() context.Context {
26 | 	return g.context
27 | }
28 | 
29 | func (g *grpcStream) Request() client.Request {
30 | 	return g.request
31 | }
32 | 
33 | func (g *grpcStream) Response() client.Response {
34 | 	return g.response
35 | }
36 | 
37 | func (g *grpcStream) Send(msg interface{}) error {
38 | 	if err := g.stream.SendMsg(msg); err != nil {
39 | 		g.setError(err)
40 | 		return err
41 | 	}
42 | 	return nil
43 | }
44 | 
45 | func (g *grpcStream) Recv(msg interface{}) (err error) {
46 | 	if err = g.stream.RecvMsg(msg); err != nil {
47 | 		if err != io.EOF {
48 | 			g.setError(err)
49 | 		}
50 | 		return err
51 | 	}
52 | 	return
53 | }
54 | 
55 | func (g *grpcStream) Error() error {
56 | 	g.RLock()
57 | 	defer g.RUnlock()
58 | 	return g.err
59 | }
60 | 
61 | func (g *grpcStream) setError(e error) {
62 | 	g.Lock()
63 | 	g.err = e
64 | 	g.Unlock()
65 | }
66 | 
67 | func (g *grpcStream) CloseSend() error {
68 | 	return g.stream.CloseSend()
69 | }
70 | 
71 | func (g *grpcStream) Close() error {
72 | 	g.Lock()
73 | 	defer g.Unlock()
74 | 
75 | 	if g.closed {
76 | 		return nil
77 | 	}
78 | 	// cancel the context
79 | 	g.cancel()
80 | 	g.closed = true
81 | 	// release back to pool
82 | 	g.release(g.err)
83 | 	return nil
84 | }
85 | 


--------------------------------------------------------------------------------
/client/retry.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/errors"
 7 | )
 8 | 
 9 | // note that returning either false or a non-nil error will result in the call not being retried.
10 | type RetryFunc func(ctx context.Context, req Request, retryCount int, err error) (bool, error)
11 | 
12 | // RetryAlways always retry on error.
13 | func RetryAlways(ctx context.Context, req Request, retryCount int, err error) (bool, error) {
14 | 	return true, nil
15 | }
16 | 
17 | // RetryOnError retries a request on a 500 or timeout error.
18 | func RetryOnError(ctx context.Context, req Request, retryCount int, err error) (bool, error) {
19 | 	if err == nil {
20 | 		return false, nil
21 | 	}
22 | 
23 | 	e := errors.Parse(err.Error())
24 | 	if e == nil {
25 | 		return false, nil
26 | 	}
27 | 
28 | 	switch e.Code {
29 | 	// Retry on timeout, not on 500 internal server error, as that is a business
30 | 	// logic error that should be handled by the user.
31 | 	case 408:
32 | 		return true, nil
33 | 	default:
34 | 		return false, nil
35 | 	}
36 | }
37 | 


--------------------------------------------------------------------------------
/client/rpc_message.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | type message struct {
 4 | 	payload     interface{}
 5 | 	topic       string
 6 | 	contentType string
 7 | }
 8 | 
 9 | func newMessage(topic string, payload interface{}, contentType string, opts ...MessageOption) Message {
10 | 	var options MessageOptions
11 | 	for _, o := range opts {
12 | 		o(&options)
13 | 	}
14 | 
15 | 	if len(options.ContentType) > 0 {
16 | 		contentType = options.ContentType
17 | 	}
18 | 
19 | 	return &message{
20 | 		payload:     payload,
21 | 		topic:       topic,
22 | 		contentType: contentType,
23 | 	}
24 | }
25 | 
26 | func (m *message) ContentType() string {
27 | 	return m.contentType
28 | }
29 | 
30 | func (m *message) Topic() string {
31 | 	return m.topic
32 | }
33 | 
34 | func (m *message) Payload() interface{} {
35 | 	return m.payload
36 | }
37 | 


--------------------------------------------------------------------------------
/client/rpc_request.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/codec"
 5 | )
 6 | 
 7 | type rpcRequest struct {
 8 | 	opts        RequestOptions
 9 | 	codec       codec.Codec
10 | 	body        interface{}
11 | 	service     string
12 | 	method      string
13 | 	endpoint    string
14 | 	contentType string
15 | }
16 | 
17 | func newRequest(service, endpoint string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
18 | 	var opts RequestOptions
19 | 
20 | 	for _, o := range reqOpts {
21 | 		o(&opts)
22 | 	}
23 | 
24 | 	// set the content-type specified
25 | 	if len(opts.ContentType) > 0 {
26 | 		contentType = opts.ContentType
27 | 	}
28 | 
29 | 	return &rpcRequest{
30 | 		service:     service,
31 | 		method:      endpoint,
32 | 		endpoint:    endpoint,
33 | 		body:        request,
34 | 		contentType: contentType,
35 | 		opts:        opts,
36 | 	}
37 | }
38 | 
39 | func (r *rpcRequest) ContentType() string {
40 | 	return r.contentType
41 | }
42 | 
43 | func (r *rpcRequest) Service() string {
44 | 	return r.service
45 | }
46 | 
47 | func (r *rpcRequest) Method() string {
48 | 	return r.method
49 | }
50 | 
51 | func (r *rpcRequest) Endpoint() string {
52 | 	return r.endpoint
53 | }
54 | 
55 | func (r *rpcRequest) Body() interface{} {
56 | 	return r.body
57 | }
58 | 
59 | func (r *rpcRequest) Codec() codec.Writer {
60 | 	return r.codec
61 | }
62 | 
63 | func (r *rpcRequest) Stream() bool {
64 | 	return r.opts.Stream
65 | }
66 | 


--------------------------------------------------------------------------------
/client/rpc_request_test.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"testing"
 5 | )
 6 | 
 7 | func TestRequestOptions(t *testing.T) {
 8 | 	r := newRequest("service", "endpoint", nil, "application/json")
 9 | 	if r.Service() != "service" {
10 | 		t.Fatalf("expected 'service' got %s", r.Service())
11 | 	}
12 | 	if r.Endpoint() != "endpoint" {
13 | 		t.Fatalf("expected 'endpoint' got %s", r.Endpoint())
14 | 	}
15 | 	if r.ContentType() != "application/json" {
16 | 		t.Fatalf("expected 'endpoint' got %s", r.ContentType())
17 | 	}
18 | 
19 | 	r2 := newRequest("service", "endpoint", nil, "application/json", WithContentType("application/protobuf"))
20 | 	if r2.ContentType() != "application/protobuf" {
21 | 		t.Fatalf("expected 'endpoint' got %s", r2.ContentType())
22 | 	}
23 | }
24 | 


--------------------------------------------------------------------------------
/client/rpc_response.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/codec"
 5 | 	"go-micro.dev/v5/transport"
 6 | )
 7 | 
 8 | type rpcResponse struct {
 9 | 	socket transport.Socket
10 | 	codec  codec.Codec
11 | 	header map[string]string
12 | 	body   []byte
13 | }
14 | 
15 | func (r *rpcResponse) Codec() codec.Reader {
16 | 	return r.codec
17 | }
18 | 
19 | func (r *rpcResponse) Header() map[string]string {
20 | 	return r.header
21 | }
22 | 
23 | func (r *rpcResponse) Read() ([]byte, error) {
24 | 	var msg transport.Message
25 | 
26 | 	if err := r.socket.Recv(&msg); err != nil {
27 | 		return nil, err
28 | 	}
29 | 
30 | 	// set internals
31 | 	r.header = msg.Header
32 | 	r.body = msg.Body
33 | 
34 | 	return msg.Body, nil
35 | }
36 | 


--------------------------------------------------------------------------------
/client/wrapper.go:
--------------------------------------------------------------------------------
 1 | package client
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/registry"
 7 | )
 8 | 
 9 | // CallFunc represents the individual call func.
10 | type CallFunc func(ctx context.Context, node *registry.Node, req Request, rsp interface{}, opts CallOptions) error
11 | 
12 | // CallWrapper is a low level wrapper for the CallFunc.
13 | type CallWrapper func(CallFunc) CallFunc
14 | 
15 | // Wrapper wraps a client and returns a client.
16 | type Wrapper func(Client) Client
17 | 
18 | // StreamWrapper wraps a Stream and returns the equivalent.
19 | type StreamWrapper func(Stream) Stream
20 | 


--------------------------------------------------------------------------------
/cmd/micro/cli/new/template/handler.go:
--------------------------------------------------------------------------------
 1 | package template
 2 | 
 3 | var (
 4 | 	HandlerSRV = `package handler
 5 | 
 6 | import (
 7 | 	"context"
 8 | 
 9 | 	log "go-micro.dev/v5/logger"
10 | 
11 | 	pb "{{.Dir}}/proto"
12 | )
13 | 
14 | type {{title .Alias}} struct{}
15 | 
16 | // Return a new handler
17 | func New() *{{title .Alias}} {
18 | 	return &{{title .Alias}}{}
19 | }
20 | 
21 | // Call is a single request handler called via client.Call or the generated client code
22 | func (e *{{title .Alias}}) Call(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
23 | 	log.Info("Received {{title .Alias}}.Call request")
24 | 	rsp.Msg = "Hello " + req.Name
25 | 	return nil
26 | }
27 | 
28 | // Stream is a server side stream handler called via client.Stream or the generated client code
29 | func (e *{{title .Alias}}) Stream(ctx context.Context, req *pb.StreamingRequest, stream pb.{{title .Alias}}_StreamStream) error {
30 | 	log.Infof("Received {{title .Alias}}.Stream request with count: %d", req.Count)
31 | 
32 | 	for i := 0; i < int(req.Count); i++ {
33 | 		log.Infof("Responding: %d", i)
34 | 		if err := stream.Send(&pb.StreamingResponse{
35 | 			Count: int64(i),
36 | 		}); err != nil {
37 | 			return err
38 | 		}
39 | 	}
40 | 
41 | 	return nil
42 | }
43 | `
44 | 
45 | 	SubscriberSRV = `package subscriber
46 | 
47 | import (
48 | 	"context"
49 | 	log "go-micro.dev/v5/logger"
50 | 
51 | 	pb "{{.Dir}}/proto"
52 | )
53 | 
54 | type {{title .Alias}} struct{}
55 | 
56 | func (e *{{title .Alias}}) Handle(ctx context.Context, msg *pb.Message) error {
57 | 	log.Info("Handler Received message: ", msg.Say)
58 | 	return nil
59 | }
60 | 
61 | func Handler(ctx context.Context, msg *pb.Message) error {
62 | 	log.Info("Function Received message: ", msg.Say)
63 | 	return nil
64 | }
65 | `
66 | )
67 | 


--------------------------------------------------------------------------------
/cmd/micro/cli/new/template/ignore.go:
--------------------------------------------------------------------------------
1 | package template
2 | 
3 | var (
4 | 	GitIgnore = `
5 | {{.Alias}}
6 | `
7 | )
8 | 


--------------------------------------------------------------------------------
/cmd/micro/cli/new/template/main.go:
--------------------------------------------------------------------------------
 1 | package template
 2 | 
 3 | var (
 4 | 	MainSRV = `package main
 5 | 
 6 | import (
 7 | 	"{{.Dir}}/handler"
 8 | 	pb "{{.Dir}}/proto"
 9 | 
10 | 	"go-micro.dev/v5"
11 | )
12 | 
13 | func main() {
14 | 	// Create service
15 | 	service := micro.New("{{lower .Alias}}")
16 | 
17 | 	// Initialize service
18 | 	service.Init()
19 | 
20 | 	// Register handler
21 | 	pb.Register{{title .Alias}}Handler(service.Server(), handler.New())
22 | 
23 | 	// Run service
24 | 	service.Run()
25 | }
26 | `
27 | )
28 | 


--------------------------------------------------------------------------------
/cmd/micro/cli/new/template/makefile.go:
--------------------------------------------------------------------------------
 1 | package template
 2 | 
 3 | var (
 4 | 	Makefile = `
 5 | GOPATH:=$(shell go env GOPATH)
 6 | .PHONY: init
 7 | init:
 8 | 	go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
 9 | 	go install go-micro.dev/v5/cmd/protoc-gen-micro@latest
10 | 	go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest
11 | 
12 | .PHONY: api
13 | api:
14 | 	protoc --openapi_out=. --proto_path=. proto/{{.Alias}}.proto
15 | 
16 | .PHONY: proto
17 | proto:
18 | 	protoc --proto_path=. --micro_out=. --go_out=:. proto/{{.Alias}}.proto
19 | 	
20 | .PHONY: build
21 | build:
22 | 	go build -o {{.Alias}} *.go
23 | 
24 | .PHONY: test
25 | test:
26 | 	go test -v ./... -cover
27 | 
28 | .PHONY: docker
29 | docker:
30 | 	docker build . -t {{.Alias}}:latest
31 | `
32 | )
33 | 


--------------------------------------------------------------------------------
/cmd/micro/cli/new/template/module.go:
--------------------------------------------------------------------------------
 1 | package template
 2 | 
 3 | var (
 4 | 	Module = `module {{.Dir}}
 5 | 
 6 | go 1.18
 7 | 
 8 | require (
 9 | 	go-micro.dev/v5 latest
10 | 	github.com/golang/protobuf latest
11 | 	google.golang.org/protobuf latest
12 | )
13 | `
14 | )
15 | 


--------------------------------------------------------------------------------
/cmd/micro/cli/new/template/proto.go:
--------------------------------------------------------------------------------
 1 | package template
 2 | 
 3 | var (
 4 | 	ProtoSRV = `syntax = "proto3";
 5 | 
 6 | package {{dehyphen .Alias}};
 7 | 
 8 | option go_package = "./proto;{{dehyphen .Alias}}";
 9 | 
10 | service {{title .Alias}} {
11 | 	rpc Call(Request) returns (Response) {}
12 | 	rpc Stream(StreamingRequest) returns (stream StreamingResponse) {}
13 | }
14 | 
15 | message Message {
16 | 	string say = 1;
17 | }
18 | 
19 | message Request {
20 | 	string name = 1;
21 | }
22 | 
23 | message Response {
24 | 	string msg = 1;
25 | }
26 | 
27 | message StreamingRequest {
28 | 	int64 count = 1;
29 | }
30 | 
31 | message StreamingResponse {
32 | 	int64 count = 1;
33 | }
34 | `
35 | )
36 | 


--------------------------------------------------------------------------------
/cmd/micro/cli/new/template/readme.go:
--------------------------------------------------------------------------------
 1 | package template
 2 | 
 3 | var (
 4 | 	Readme = `# {{title .Alias}} Service
 5 | 
 6 | This is the {{title .Alias}} service
 7 | 
 8 | Generated with
 9 | 
10 | ` + "```" +
11 | 		`
12 | micro new {{.Alias}}
13 | ` + "```" + `
14 | 
15 | ## Usage
16 | 
17 | Generate the proto code
18 | 
19 | ` + "```" +
20 | 		`
21 | make proto
22 | ` + "```" + `
23 | 
24 | Run the service
25 | 
26 | ` + "```" +
27 | 		`
28 | micro run .
29 | ` + "```"
30 | )
31 | 


--------------------------------------------------------------------------------
/cmd/micro/main.go:
--------------------------------------------------------------------------------
 1 | package main
 2 | 
 3 | import (
 4 | 	"embed"
 5 | 	"go-micro.dev/v5/cmd"
 6 | 
 7 | 	_ "go-micro.dev/v5/cmd/micro/cli"
 8 | 	_ "go-micro.dev/v5/cmd/micro/run"
 9 | 	"go-micro.dev/v5/cmd/micro/server"
10 | )
11 | 
12 | //go:embed web/styles.css web/main.js web/templates/*
13 | var webFS embed.FS
14 | 
15 | var version = "5.0.0-dev"
16 | 
17 | func init() {
18 | 	server.HTML = webFS
19 | }
20 | 
21 | func main() {
22 | 	cmd.Init(
23 | 		cmd.Name("micro"),
24 | 		cmd.Version(version),
25 | 	)
26 | }
27 | 


--------------------------------------------------------------------------------
/cmd/micro/web/main.js:
--------------------------------------------------------------------------------
 1 | // Minimal JS for reactive form submissions
 2 | 
 3 | document.addEventListener('DOMContentLoaded', function() {
 4 |     document.querySelectorAll('form[data-reactive]')?.forEach(function(form) {
 5 |         form.addEventListener('submit', async function(e) {
 6 |             e.preventDefault();
 7 |             const formData = new FormData(form);
 8 |             const params = {};
 9 |             for (const [key, value] of formData.entries()) {
10 |                 params[key] = value;
11 |             }
12 |             const action = form.getAttribute('action');
13 |             const method = form.getAttribute('method') || 'POST';
14 |             try {
15 |                 const resp = await fetch(action, {
16 |                     method,
17 |                     headers: { 'Content-Type': 'application/json' },
18 |                     body: JSON.stringify(params)
19 |                 });
20 |                 const data = await resp.json();
21 |                 // Find or create a response container
22 |                 let respDiv = form.querySelector('.js-response');
23 |                 if (!respDiv) {
24 |                     respDiv = document.createElement('div');
25 |                     respDiv.className = 'js-response';
26 |                     form.appendChild(respDiv);
27 |                 }
28 |                 respDiv.innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
29 |             } catch (err) {
30 |                 alert('Error: ' + err);
31 |             }
32 |         });
33 |     });
34 | });
35 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/auth_login.html:
--------------------------------------------------------------------------------
 1 | {{define "content"}}
 2 | <h2 class="text-2xl font-bold mb-4">Login</h2>
 3 | <form method="POST" action="/auth/login" style="max-width:340px; margin:2em 0;">
 4 |   <div style="margin-bottom:1.2em;">
 5 |     <input name="id" placeholder="Username" required style="width:100%; padding:0.7em;">
 6 |   </div>
 7 |   <div style="margin-bottom:1.2em;">
 8 |     <input name="password" type="password" placeholder="Password" required style="width:100%; padding:0.7em;">
 9 |   </div>
10 |   <button type="submit" style="width:100%; padding:0.7em;">Login</button>
11 | </form>
12 | {{if .Error}}
13 |   <div style="color:#c00; margin-top:1em;">{{.Error}}</div>
14 | {{end}}
15 | {{end}}
16 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/auth_users.html:
--------------------------------------------------------------------------------
 1 | {{define "content"}}
 2 | <h2 class="text-2xl font-bold mb-4">User Accounts</h2>
 3 | <table style="margin-bottom:2em;">
 4 |   <thead>
 5 |     <tr><th>ID</th><th>Type</th><th>Scopes</th><th>Metadata</th><th>Delete</th></tr>
 6 |   </thead>
 7 |   <tbody>
 8 |     {{range .Users}}
 9 |     <tr>
10 |       <td>{{.ID}}</td>
11 |       <td>{{.Type}}</td>
12 |       <td>{{range .Scopes}}<code>{{.}}</code> {{end}}</td>
13 |       <td>
14 |         {{range $k, $v := .Metadata}}
15 |           {{if ne $k "password_hash"}}
16 |             <b>{{$k}}</b>: {{$v}} 
17 |           {{end}}
18 |         {{end}}
19 |       </td>
20 |       <td>
21 |         <form method="POST" action="/auth/users" style="display:inline; padding: 0; border: 0">
22 |           <input type="hidden" name="delete" value="{{.ID}}">
23 |           <button type="submit" onclick="return confirm('Delete user {{.ID}}?')">Delete</button>
24 |         </form>
25 |       </td>
26 |     </tr>
27 |     {{end}}
28 |   </tbody>
29 | </table>
30 | <h3 style="margin-bottom:1em;">Create New User</h3>
31 | <form method="POST" action="/auth/users">
32 |   <input name="id" placeholder="Username" required style="margin-right:1em;">
33 |   <input name="password" type="password" placeholder="Password" required style="margin-right:1em;">
34 |   <select name="type" style="margin-right:1em;">
35 |     <option value="user">User</option>
36 |     <option value="admin">Admin</option>
37 |   </select>
38 |   <button type="submit">Create</button>
39 | </form>
40 | {{end}}
41 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/form.html:
--------------------------------------------------------------------------------
 1 | {{define "content"}}
 2 | <h2>{{.ServiceName}}</h2>
 3 | <form action="/{{.Action}}" method="POST" data-reactive>
 4 |     <h3 class="text-lg font-bold mb-2">{{.EndpointName}}</h3>
 5 |     {{range .Inputs}}
 6 |         <label class="block font-semibold">{{.Label}}</label>
 7 |         <input name="{{.Name}}" placeholder="{{.Placeholder}}" class="border rounded px-2 py-1 mb-2 w-full" value="{{.Value}}">
 8 |     {{end}}
 9 |     <button class="micro-link mt-2" type="submit">Submit</button>
10 |     <div class="js-response"></div>
11 | </form>
12 | {{if .Error}}
13 |     <div class="mt-4 text-red-600 font-bold">Error: {{.Error}}</div>
14 | {{end}}
15 | {{if .Response}}
16 |     <div class="mt-4">
17 |         <h4 class="font-bold mb-2">Response</h4>
18 |         {{.ResponseTable}}
19 |         <pre class="bg-gray-100 rounded p-2 mt-2">{{.ResponseJSON}}</pre>
20 |     </div>
21 | {{end}}
22 | <script src="/main.js"></script>
23 | {{end}}
24 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/home.html:
--------------------------------------------------------------------------------
 1 | {{define "content"}}
 2 | <h2 class="text-2xl font-bold mb-4">Dashboard</h2>
 3 | <div style="display:flex; align-items:center; gap:2em; margin-bottom:2em;">
 4 |   <div style="display:flex; align-items:center; gap:0.5em;">
 5 |     <span style="font-size:2.2em; vertical-align:middle;">
 6 |       {{if eq .StatusDot "green"}}
 7 |         <span style="display:inline-block; width:1em; height:1em; background:#2ecc40; border-radius:50%;"></span>
 8 |       {{else if eq .StatusDot "yellow"}}
 9 |         <span style="display:inline-block; width:1em; height:1em; background:#ffcc00; border-radius:50%;"></span>
10 |       {{else}}
11 |         <span style="display:inline-block; width:1em; height:1em; background:#ff4136; border-radius:50%;"></span>
12 |       {{end}}
13 |     </span>
14 |     <span style="font-size:1.2em; font-weight:bold;">Status</span>
15 |   </div>
16 |   <div style="font-size:1.1em;">Services: <b>{{.ServiceCount}}</b></div>
17 |   <div style="font-size:1.1em; color:#2ecc40;">Running: <b>{{.RunningCount}}</b></div>
18 |   <div style="font-size:1.1em; color:#ff4136;">Stopped: <b>{{.StoppedCount}}</b></div>
19 | </div>
20 | <p>Welcome to the Micro dashboard. Use the sidebar to navigate services, logs, status, and API.</p>
21 | {{end}}
22 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/log.html:
--------------------------------------------------------------------------------
1 | {{define "content"}}
2 | <h2 class="text-2xl font-bold mb-4">Logs for {{.Service}}</h2>
3 | <pre class="bg-gray-100 rounded p-2 mt-2" style="max-height: 60vh; overflow-y: auto;">{{.Log}}</pre>
4 | <a href="/logs" class="micro-link">Back to logs</a>
5 | {{end}}
6 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/logs.html:
--------------------------------------------------------------------------------
1 | {{define "content"}}
2 | <h2 class="text-2xl font-bold mb-4">Logs</h2>
3 | <ul class="no-bullets">
4 |     {{range .Services}}
5 |         <li><a href="/logs/{{.}}" class="micro-link">{{.}}</a></li>
6 |     {{end}}
7 | </ul>
8 | {{end}}
9 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/service.html:
--------------------------------------------------------------------------------
 1 | {{define "content"}}
 2 | {{if .ServiceName}}
 3 |   <h2 class="text-xl font-bold mb-2">{{.ServiceName}}</h2>
 4 |   <h4 class="font-semibold mb-2">Endpoints</h4>
 5 |   {{if .Endpoints}}
 6 |       {{range .Endpoints}}
 7 |       <div><a href="{{.Path}}" class="micro-link">{{.Name}}</a></div>
 8 |       {{end}}
 9 |   {{else}}
10 |       <p>No endpoints registered</p>
11 |   {{end}}
12 |   <h4 class="font-semibold mt-4 mb-2">Description</h4>
13 |   <pre class="bg-gray-100 rounded p-2">{{.Description}}</pre>
14 | {{else}}
15 |   <h2 class="text-2xl font-bold mb-4">Services</h2>
16 |   {{if .Services}}
17 |       <ul class="no-bullets">
18 |       {{range .Services}}
19 |           <li><a href="/{{.}}" class="micro-link">{{.}}</a></li>
20 |       {{end}}
21 |       </ul>
22 |   {{else}}
23 |       <p>No services registered</p>
24 |   {{end}}
25 | {{end}}
26 | {{end}}
27 | 


--------------------------------------------------------------------------------
/cmd/micro/web/templates/status.html:
--------------------------------------------------------------------------------
 1 | {{define "content"}}
 2 | <h2 class="text-2xl font-bold mb-4">Service Status</h2>
 3 | <table>
 4 |   <thead>
 5 |     <tr>
 6 |       <th>Service</th>
 7 |       <th>Directory</th>
 8 |       <th>Status</th>
 9 |       <th>PID</th>
10 |       <th>Uptime</th>
11 |       <th>ID</th>
12 |       <th>Logs</th>
13 |     </tr>
14 |   </thead>
15 |   <tbody>
16 |     {{range .Statuses}}
17 |     <tr>
18 |       <td>{{.Service}}</td>
19 |       <td><code>{{.Dir}}</code></td>
20 |       <td>{{.Status}}</td>
21 |       <td>{{.PID}}</td>
22 |       <td>{{.Uptime}}</td>
23 |       <td style="font-size:0.9em; color:#888;">{{.ID}}</td>
24 |       <td><a href="/logs/{{.ID}}" class="log-link">View logs</a></td>
25 |     </tr>
26 |     {{end}}
27 |   </tbody>
28 | </table>
29 | {{end}}
30 | 


--------------------------------------------------------------------------------
/cmd/protoc-gen-micro/examples/greeter/greeter.proto:
--------------------------------------------------------------------------------
 1 | syntax = "proto3";
 2 | 
 3 | option go_package = "../greeter";
 4 | 
 5 | service Greeter {
 6 | 	rpc Hello(Request) returns (Response) {}
 7 | 	rpc Stream(stream Request) returns (stream Response) {}
 8 | }
 9 | 
10 | message Request {
11 | 	string name = 1;
12 | 	optional string msg = 2;
13 | }
14 | 
15 | message Response {
16 | 	string msg = 1;
17 | }
18 | 


--------------------------------------------------------------------------------
/codec/bytes/bytes.go:
--------------------------------------------------------------------------------
 1 | // Package bytes provides a bytes codec which does not encode or decode anything
 2 | package bytes
 3 | 
 4 | import (
 5 | 	"fmt"
 6 | 	"io"
 7 | 
 8 | 	"go-micro.dev/v5/codec"
 9 | )
10 | 
11 | type Codec struct {
12 | 	Conn io.ReadWriteCloser
13 | }
14 | 
15 | // Frame gives us the ability to define raw data to send over the pipes.
16 | type Frame struct {
17 | 	Data []byte
18 | }
19 | 
20 | func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error {
21 | 	return nil
22 | }
23 | 
24 | func (c *Codec) ReadBody(b interface{}) error {
25 | 	// read bytes
26 | 	buf, err := io.ReadAll(c.Conn)
27 | 	if err != nil {
28 | 		return err
29 | 	}
30 | 
31 | 	switch v := b.(type) {
32 | 	case *[]byte:
33 | 		*v = buf
34 | 	case *Frame:
35 | 		v.Data = buf
36 | 	default:
37 | 		return fmt.Errorf("failed to read body: %v is not type of *[]byte", b)
38 | 	}
39 | 
40 | 	return nil
41 | }
42 | 
43 | func (c *Codec) Write(m *codec.Message, b interface{}) error {
44 | 	var v []byte
45 | 	switch vb := b.(type) {
46 | 	case *Frame:
47 | 		v = vb.Data
48 | 	case *[]byte:
49 | 		v = *vb
50 | 	case []byte:
51 | 		v = vb
52 | 	default:
53 | 		return fmt.Errorf("failed to write: %v is not type of *[]byte or []byte", b)
54 | 	}
55 | 	_, err := c.Conn.Write(v)
56 | 	return err
57 | }
58 | 
59 | func (c *Codec) Close() error {
60 | 	return c.Conn.Close()
61 | }
62 | 
63 | func (c *Codec) String() string {
64 | 	return "bytes"
65 | }
66 | 
67 | func NewCodec(c io.ReadWriteCloser) codec.Codec {
68 | 	return &Codec{
69 | 		Conn: c,
70 | 	}
71 | }
72 | 


--------------------------------------------------------------------------------
/codec/bytes/marshaler.go:
--------------------------------------------------------------------------------
 1 | package bytes
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/codec"
 5 | )
 6 | 
 7 | type Marshaler struct{}
 8 | 
 9 | type Message struct {
10 | 	Header map[string]string
11 | 	Body   []byte
12 | }
13 | 
14 | func (n Marshaler) Marshal(v interface{}) ([]byte, error) {
15 | 	switch ve := v.(type) {
16 | 	case *[]byte:
17 | 		return *ve, nil
18 | 	case []byte:
19 | 		return ve, nil
20 | 	case *Message:
21 | 		return ve.Body, nil
22 | 	}
23 | 	return nil, codec.ErrInvalidMessage
24 | }
25 | 
26 | func (n Marshaler) Unmarshal(d []byte, v interface{}) error {
27 | 	switch ve := v.(type) {
28 | 	case *[]byte:
29 | 		*ve = d
30 | 		return nil
31 | 	case *Message:
32 | 		ve.Body = d
33 | 		return nil
34 | 	}
35 | 	return codec.ErrInvalidMessage
36 | }
37 | 
38 | func (n Marshaler) String() string {
39 | 	return "bytes"
40 | }
41 | 


--------------------------------------------------------------------------------
/codec/codec.go:
--------------------------------------------------------------------------------
 1 | // Package codec is an interface for encoding messages
 2 | package codec
 3 | 
 4 | import (
 5 | 	"errors"
 6 | 	"io"
 7 | )
 8 | 
 9 | const (
10 | 	Error MessageType = iota
11 | 	Request
12 | 	Response
13 | 	Event
14 | )
15 | 
16 | var (
17 | 	ErrInvalidMessage = errors.New("invalid message")
18 | )
19 | 
20 | type MessageType int
21 | 
22 | // Takes in a connection/buffer and returns a new Codec.
23 | type NewCodec func(io.ReadWriteCloser) Codec
24 | 
25 | // Codec encodes/decodes various types of messages used within go-micro.
26 | // ReadHeader and ReadBody are called in pairs to read requests/responses
27 | // from the connection. Close is called when finished with the
28 | // connection. ReadBody may be called with a nil argument to force the
29 | // body to be read and discarded.
30 | type Codec interface {
31 | 	Reader
32 | 	Writer
33 | 	Close() error
34 | 	String() string
35 | }
36 | 
37 | type Reader interface {
38 | 	ReadHeader(*Message, MessageType) error
39 | 	ReadBody(interface{}) error
40 | }
41 | 
42 | type Writer interface {
43 | 	Write(*Message, interface{}) error
44 | }
45 | 
46 | // Marshaler is a simple encoding interface used for the broker/transport
47 | // where headers are not supported by the underlying implementation.
48 | type Marshaler interface {
49 | 	Marshal(interface{}) ([]byte, error)
50 | 	Unmarshal([]byte, interface{}) error
51 | 	String() string
52 | }
53 | 
54 | // Message represents detailed information about
55 | // the communication, likely followed by the body.
56 | // In the case of an error, body may be nil.
57 | type Message struct {
58 | 
59 | 	// The values read from the socket
60 | 	Header   map[string]string
61 | 	Id       string
62 | 	Target   string
63 | 	Method   string
64 | 	Endpoint string
65 | 	Error    string
66 | 
67 | 	Body []byte
68 | 	Type MessageType
69 | }
70 | 


--------------------------------------------------------------------------------
/codec/grpc/util.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"encoding/binary"
 5 | 	"fmt"
 6 | 	"io"
 7 | )
 8 | 
 9 | var (
10 | 	MaxMessageSize = 1024 * 1024 * 4 // 4Mb
11 | 	maxInt         = int(^uint(0) >> 1)
12 | )
13 | 
14 | func decode(r io.Reader) (uint8, []byte, error) {
15 | 	header := make([]byte, 5)
16 | 
17 | 	// read the header
18 | 	if _, err := r.Read(header); err != nil {
19 | 		return uint8(0), nil, err
20 | 	}
21 | 
22 | 	// get encoding format e.g compressed
23 | 	cf := uint8(header[0])
24 | 
25 | 	// get message length
26 | 	length := binary.BigEndian.Uint32(header[1:])
27 | 
28 | 	// no encoding format
29 | 	if length == 0 {
30 | 		return cf, nil, nil
31 | 	}
32 | 
33 | 	//
34 | 	if int64(length) > int64(maxInt) {
35 | 		return cf, nil, fmt.Errorf("grpc: received message larger than max length allowed on current machine (%d vs. %d)", length, maxInt)
36 | 	}
37 | 	if int(length) > MaxMessageSize {
38 | 		return cf, nil, fmt.Errorf("grpc: received message larger than max (%d vs. %d)", length, MaxMessageSize)
39 | 	}
40 | 
41 | 	msg := make([]byte, int(length))
42 | 
43 | 	if _, err := r.Read(msg); err != nil {
44 | 		if err == io.EOF {
45 | 			err = io.ErrUnexpectedEOF
46 | 		}
47 | 		return cf, nil, err
48 | 	}
49 | 
50 | 	return cf, msg, nil
51 | }
52 | 
53 | func encode(cf uint8, buf []byte, w io.Writer) error {
54 | 	header := make([]byte, 5)
55 | 
56 | 	// set compression
57 | 	header[0] = byte(cf)
58 | 
59 | 	// write length as header
60 | 	binary.BigEndian.PutUint32(header[1:], uint32(len(buf)))
61 | 
62 | 	// read the header
63 | 	if _, err := w.Write(header); err != nil {
64 | 		return err
65 | 	}
66 | 
67 | 	// write the buffer
68 | 	_, err := w.Write(buf)
69 | 	return err
70 | }
71 | 


--------------------------------------------------------------------------------
/codec/json/json.go:
--------------------------------------------------------------------------------
 1 | // Package json provides a json codec
 2 | package json
 3 | 
 4 | import (
 5 | 	"encoding/json"
 6 | 	"io"
 7 | 
 8 | 	"github.com/golang/protobuf/jsonpb"
 9 | 	"github.com/golang/protobuf/proto"
10 | 	"go-micro.dev/v5/codec"
11 | )
12 | 
13 | type Codec struct {
14 | 	Conn    io.ReadWriteCloser
15 | 	Encoder *json.Encoder
16 | 	Decoder *json.Decoder
17 | }
18 | 
19 | func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error {
20 | 	return nil
21 | }
22 | 
23 | func (c *Codec) ReadBody(b interface{}) error {
24 | 	if b == nil {
25 | 		return nil
26 | 	}
27 | 	if pb, ok := b.(proto.Message); ok {
28 | 		return jsonpb.UnmarshalNext(c.Decoder, pb)
29 | 	}
30 | 	return c.Decoder.Decode(b)
31 | }
32 | 
33 | func (c *Codec) Write(m *codec.Message, b interface{}) error {
34 | 	if b == nil {
35 | 		return nil
36 | 	}
37 | 	return c.Encoder.Encode(b)
38 | }
39 | 
40 | func (c *Codec) Close() error {
41 | 	return c.Conn.Close()
42 | }
43 | 
44 | func (c *Codec) String() string {
45 | 	return "json"
46 | }
47 | 
48 | func NewCodec(c io.ReadWriteCloser) codec.Codec {
49 | 	return &Codec{
50 | 		Conn:    c,
51 | 		Decoder: json.NewDecoder(c),
52 | 		Encoder: json.NewEncoder(c),
53 | 	}
54 | }
55 | 


--------------------------------------------------------------------------------
/codec/json/marshaler.go:
--------------------------------------------------------------------------------
 1 | package json
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"encoding/json"
 6 | 
 7 | 	"github.com/golang/protobuf/jsonpb"
 8 | 	"github.com/golang/protobuf/proto"
 9 | 	"github.com/oxtoacart/bpool"
10 | )
11 | 
12 | var jsonpbMarshaler = &jsonpb.Marshaler{}
13 | 
14 | // create buffer pool with 16 instances each preallocated with 256 bytes.
15 | var bufferPool = bpool.NewSizedBufferPool(16, 256)
16 | 
17 | type Marshaler struct{}
18 | 
19 | func (j Marshaler) Marshal(v interface{}) ([]byte, error) {
20 | 	if pb, ok := v.(proto.Message); ok {
21 | 		buf := bufferPool.Get()
22 | 		defer bufferPool.Put(buf)
23 | 		if err := jsonpbMarshaler.Marshal(buf, pb); err != nil {
24 | 			return nil, err
25 | 		}
26 | 		return buf.Bytes(), nil
27 | 	}
28 | 	return json.Marshal(v)
29 | }
30 | 
31 | func (j Marshaler) Unmarshal(d []byte, v interface{}) error {
32 | 	if pb, ok := v.(proto.Message); ok {
33 | 		return jsonpb.Unmarshal(bytes.NewReader(d), pb)
34 | 	}
35 | 	return json.Unmarshal(d, v)
36 | }
37 | 
38 | func (j Marshaler) String() string {
39 | 	return "json"
40 | }
41 | 


--------------------------------------------------------------------------------
/codec/proto/marshaler.go:
--------------------------------------------------------------------------------
 1 | package proto
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 
 6 | 	"github.com/golang/protobuf/proto"
 7 | 	"github.com/oxtoacart/bpool"
 8 | 	"go-micro.dev/v5/codec"
 9 | )
10 | 
11 | // create buffer pool with 16 instances each preallocated with 256 bytes.
12 | var bufferPool = bpool.NewSizedBufferPool(16, 256)
13 | 
14 | type Marshaler struct{}
15 | 
16 | func (Marshaler) Marshal(v interface{}) ([]byte, error) {
17 | 	pb, ok := v.(proto.Message)
18 | 	if !ok {
19 | 		return nil, codec.ErrInvalidMessage
20 | 	}
21 | 
22 | 	// looks not good, but allows to reuse underlining bytes
23 | 	buf := bufferPool.Get()
24 | 	pbuf := proto.NewBuffer(buf.Bytes())
25 | 	defer func() {
26 | 		bufferPool.Put(bytes.NewBuffer(pbuf.Bytes()))
27 | 	}()
28 | 
29 | 	if err := pbuf.Marshal(pb); err != nil {
30 | 		return nil, err
31 | 	}
32 | 
33 | 	return pbuf.Bytes(), nil
34 | }
35 | 
36 | func (Marshaler) Unmarshal(data []byte, v interface{}) error {
37 | 	pb, ok := v.(proto.Message)
38 | 	if !ok {
39 | 		return codec.ErrInvalidMessage
40 | 	}
41 | 
42 | 	return proto.Unmarshal(data, pb)
43 | }
44 | 
45 | func (Marshaler) String() string {
46 | 	return "proto"
47 | }
48 | 


--------------------------------------------------------------------------------
/codec/proto/message.go:
--------------------------------------------------------------------------------
 1 | package proto
 2 | 
 3 | type Message struct {
 4 | 	Data []byte
 5 | }
 6 | 
 7 | func (m *Message) MarshalJSON() ([]byte, error) {
 8 | 	return m.Data, nil
 9 | }
10 | 
11 | func (m *Message) UnmarshalJSON(data []byte) error {
12 | 	m.Data = data
13 | 	return nil
14 | }
15 | 
16 | func (m *Message) ProtoMessage() {}
17 | 
18 | func (m *Message) Reset() {
19 | 	*m = Message{}
20 | }
21 | 
22 | func (m *Message) String() string {
23 | 	return string(m.Data)
24 | }
25 | 
26 | func (m *Message) Marshal() ([]byte, error) {
27 | 	return m.Data, nil
28 | }
29 | 
30 | func (m *Message) Unmarshal(data []byte) error {
31 | 	m.Data = data
32 | 	return nil
33 | }
34 | 
35 | func NewMessage(data []byte) *Message {
36 | 	return &Message{data}
37 | }
38 | 


--------------------------------------------------------------------------------
/codec/proto/proto.go:
--------------------------------------------------------------------------------
 1 | // Package proto provides a proto codec
 2 | package proto
 3 | 
 4 | import (
 5 | 	"io"
 6 | 
 7 | 	"github.com/golang/protobuf/proto"
 8 | 	"go-micro.dev/v5/codec"
 9 | )
10 | 
11 | type Codec struct {
12 | 	Conn io.ReadWriteCloser
13 | }
14 | 
15 | func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error {
16 | 	return nil
17 | }
18 | 
19 | func (c *Codec) ReadBody(b interface{}) error {
20 | 	if b == nil {
21 | 		return nil
22 | 	}
23 | 	buf, err := io.ReadAll(c.Conn)
24 | 	if err != nil {
25 | 		return err
26 | 	}
27 | 	m, ok := b.(proto.Message)
28 | 	if !ok {
29 | 		return codec.ErrInvalidMessage
30 | 	}
31 | 	return proto.Unmarshal(buf, m)
32 | }
33 | 
34 | func (c *Codec) Write(m *codec.Message, b interface{}) error {
35 | 	if b == nil {
36 | 		// Nothing to write
37 | 		return nil
38 | 	}
39 | 	p, ok := b.(proto.Message)
40 | 	if !ok {
41 | 		return codec.ErrInvalidMessage
42 | 	}
43 | 	buf, err := proto.Marshal(p)
44 | 	if err != nil {
45 | 		return err
46 | 	}
47 | 	_, err = c.Conn.Write(buf)
48 | 	return err
49 | }
50 | 
51 | func (c *Codec) Close() error {
52 | 	return c.Conn.Close()
53 | }
54 | 
55 | func (c *Codec) String() string {
56 | 	return "proto"
57 | }
58 | 
59 | func NewCodec(c io.ReadWriteCloser) codec.Codec {
60 | 	return &Codec{
61 | 		Conn: c,
62 | 	}
63 | }
64 | 


--------------------------------------------------------------------------------
/codec/protorpc/envelope.pb.micro.go:
--------------------------------------------------------------------------------
 1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
 2 | // source: codec/protorpc/envelope.proto
 3 | 
 4 | package protorpc
 5 | 
 6 | import (
 7 | 	fmt "fmt"
 8 | 	proto "github.com/golang/protobuf/proto"
 9 | 	math "math"
10 | )
11 | 
12 | // Reference imports to suppress errors if they are not otherwise used.
13 | var _ = proto.Marshal
14 | var _ = fmt.Errorf
15 | var _ = math.Inf
16 | 
17 | // This is a compile-time assertion to ensure that this generated file
18 | // is compatible with the proto package it is being compiled against.
19 | // A compilation error at this line likely means your copy of the
20 | // proto package needs to be updated.
21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
22 | 


--------------------------------------------------------------------------------
/codec/protorpc/envelope.proto:
--------------------------------------------------------------------------------
 1 | syntax = "proto3";
 2 | 
 3 | package protorpc;
 4 | 
 5 | message Request {
 6 | 	string service_method = 1;
 7 | 	fixed64 seq = 2;
 8 | }
 9 | 
10 | message Response {
11 | 	string service_method = 1;
12 | 	fixed64 seq = 2;
13 | 	string error = 3;
14 | }
15 | 


--------------------------------------------------------------------------------
/codec/protorpc/netstring.go:
--------------------------------------------------------------------------------
 1 | package protorpc
 2 | 
 3 | import (
 4 | 	"encoding/binary"
 5 | 	"io"
 6 | )
 7 | 
 8 | // WriteNetString writes data to a big-endian netstring on a Writer.
 9 | // Size is always a 32-bit unsigned int.
10 | func WriteNetString(w io.Writer, data []byte) (written int, err error) {
11 | 	size := make([]byte, 4)
12 | 	binary.BigEndian.PutUint32(size, uint32(len(data)))
13 | 	if written, err = w.Write(size); err != nil {
14 | 		return
15 | 	}
16 | 	return w.Write(data)
17 | }
18 | 
19 | // ReadNetString reads data from a big-endian netstring.
20 | func ReadNetString(r io.Reader) (data []byte, err error) {
21 | 	sizeBuf := make([]byte, 4)
22 | 	_, err = r.Read(sizeBuf)
23 | 	if err != nil {
24 | 		return nil, err
25 | 	}
26 | 	size := binary.BigEndian.Uint32(sizeBuf)
27 | 	if size == 0 {
28 | 		return nil, nil
29 | 	}
30 | 	data = make([]byte, size)
31 | 	_, err = r.Read(data)
32 | 	if err != nil {
33 | 		return nil, err
34 | 	}
35 | 	return
36 | }
37 | 


--------------------------------------------------------------------------------
/codec/text/text.go:
--------------------------------------------------------------------------------
 1 | // Package text reads any text/* content-type
 2 | package text
 3 | 
 4 | import (
 5 | 	"fmt"
 6 | 	"io"
 7 | 
 8 | 	"go-micro.dev/v5/codec"
 9 | )
10 | 
11 | type Codec struct {
12 | 	Conn io.ReadWriteCloser
13 | }
14 | 
15 | // Frame gives us the ability to define raw data to send over the pipes.
16 | type Frame struct {
17 | 	Data []byte
18 | }
19 | 
20 | func (c *Codec) ReadHeader(m *codec.Message, t codec.MessageType) error {
21 | 	return nil
22 | }
23 | 
24 | func (c *Codec) ReadBody(b interface{}) error {
25 | 	// read bytes
26 | 	buf, err := io.ReadAll(c.Conn)
27 | 	if err != nil {
28 | 		return err
29 | 	}
30 | 
31 | 	switch v := b.(type) {
32 | 	case *string:
33 | 		*v = string(buf)
34 | 	case *[]byte:
35 | 		*v = buf
36 | 	case *Frame:
37 | 		v.Data = buf
38 | 	default:
39 | 		return fmt.Errorf("failed to read body: %v is not type of *[]byte", b)
40 | 	}
41 | 
42 | 	return nil
43 | }
44 | 
45 | func (c *Codec) Write(m *codec.Message, b interface{}) error {
46 | 	var v []byte
47 | 	switch ve := b.(type) {
48 | 	case *Frame:
49 | 		v = ve.Data
50 | 	case *[]byte:
51 | 		v = *ve
52 | 	case *string:
53 | 		v = []byte(*ve)
54 | 	case string:
55 | 		v = []byte(ve)
56 | 	case []byte:
57 | 		v = ve
58 | 	default:
59 | 		return fmt.Errorf("failed to write: %v is not type of *[]byte or []byte", b)
60 | 	}
61 | 	_, err := c.Conn.Write(v)
62 | 	return err
63 | }
64 | 
65 | func (c *Codec) Close() error {
66 | 	return c.Conn.Close()
67 | }
68 | 
69 | func (c *Codec) String() string {
70 | 	return "text"
71 | }
72 | 
73 | func NewCodec(c io.ReadWriteCloser) codec.Codec {
74 | 	return &Codec{
75 | 		Conn: c,
76 | 	}
77 | }
78 | 


--------------------------------------------------------------------------------
/config/README.md:
--------------------------------------------------------------------------------
 1 | # Config [![GoDoc](https://godoc.org/github.com/micro/go-micro/config?status.svg)](https://godoc.org/github.com/micro/go-micro/config)
 2 | 
 3 | Config is a pluggable dynamic config package
 4 | 
 5 | Most config in applications are statically configured or include complex logic to load from multiple sources.
 6 | Go Config makes this easy, pluggable and mergeable. You'll never have to deal with config in the same way again.
 7 | 
 8 | ## Features
 9 | 
10 | - **Dynamic Loading** - Load configuration from multiple source as and when needed. Go Config manages watching config sources
11 |   in the background and automatically merges and updates an in memory view.
12 | 
13 | - **Pluggable Sources** - Choose from any number of sources to load and merge config. The backend source is abstracted away into
14 |   a standard format consumed internally and decoded via encoders. Sources can be env vars, flags, file, etcd, k8s configmap, etc.
15 | 
16 | - **Mergeable Config** - If you specify multiple sources of config, regardless of format, they will be merged and presented in
17 |   a single view. This massively simplifies priority order loading and changes based on environment.
18 | 
19 | - **Observe Changes** - Optionally watch the config for changes to specific values. Hot reload your app using Go Config's watcher.
20 |   You don't have to handle ad-hoc hup reloading or whatever else, just keep reading the config and watch for changes if you need
21 |   to be notified.
22 | 
23 | - **Sane Defaults** - In case config loads badly or is completely wiped away for some unknown reason, you can specify fallback
24 |   values when accessing any config values directly. This ensures you'll always be reading some sane default in the event of a problem.
25 | 


--------------------------------------------------------------------------------
/config/encoder/encoder.go:
--------------------------------------------------------------------------------
1 | // Package encoder handles source encoding formats
2 | package encoder
3 | 
4 | type Encoder interface {
5 | 	Encode(interface{}) ([]byte, error)
6 | 	Decode([]byte, interface{}) error
7 | 	String() string
8 | }
9 | 


--------------------------------------------------------------------------------
/config/encoder/json/json.go:
--------------------------------------------------------------------------------
 1 | package json
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 
 6 | 	"go-micro.dev/v5/config/encoder"
 7 | )
 8 | 
 9 | type jsonEncoder struct{}
10 | 
11 | func (j jsonEncoder) Encode(v interface{}) ([]byte, error) {
12 | 	return json.Marshal(v)
13 | }
14 | 
15 | func (j jsonEncoder) Decode(d []byte, v interface{}) error {
16 | 	return json.Unmarshal(d, v)
17 | }
18 | 
19 | func (j jsonEncoder) String() string {
20 | 	return "json"
21 | }
22 | 
23 | func NewEncoder() encoder.Encoder {
24 | 	return jsonEncoder{}
25 | }
26 | 


--------------------------------------------------------------------------------
/config/loader/loader.go:
--------------------------------------------------------------------------------
 1 | // Package loader manages loading from multiple sources
 2 | package loader
 3 | 
 4 | import (
 5 | 	"context"
 6 | 
 7 | 	"go-micro.dev/v5/config/reader"
 8 | 	"go-micro.dev/v5/config/source"
 9 | )
10 | 
11 | // Loader manages loading sources.
12 | type Loader interface {
13 | 	// Stop the loader
14 | 	Close() error
15 | 	// Load the sources
16 | 	Load(...source.Source) error
17 | 	// A Snapshot of loaded config
18 | 	Snapshot() (*Snapshot, error)
19 | 	// Force sync of sources
20 | 	Sync() error
21 | 	// Watch for changes
22 | 	Watch(...string) (Watcher, error)
23 | 	// Name of loader
24 | 	String() string
25 | }
26 | 
27 | // Watcher lets you watch sources and returns a merged ChangeSet.
28 | type Watcher interface {
29 | 	// First call to next may return the current Snapshot
30 | 	// If you are watching a path then only the data from
31 | 	// that path is returned.
32 | 	Next() (*Snapshot, error)
33 | 	// Stop watching for changes
34 | 	Stop() error
35 | }
36 | 
37 | // Snapshot is a merged ChangeSet.
38 | type Snapshot struct {
39 | 	// The merged ChangeSet
40 | 	ChangeSet *source.ChangeSet
41 | 	// Deterministic and comparable version of the snapshot
42 | 	Version string
43 | }
44 | 
45 | // Options contains all options for a config loader.
46 | type Options struct {
47 | 	Reader reader.Reader
48 | 
49 | 	// for alternative data
50 | 	Context context.Context
51 | 
52 | 	Source []source.Source
53 | 
54 | 	WithWatcherDisabled bool
55 | }
56 | 
57 | // Option is a helper for a single option.
58 | type Option func(o *Options)
59 | 
60 | // Copy snapshot.
61 | func Copy(s *Snapshot) *Snapshot {
62 | 	cs := *(s.ChangeSet)
63 | 
64 | 	return &Snapshot{
65 | 		ChangeSet: &cs,
66 | 		Version:   s.Version,
67 | 	}
68 | }
69 | 


--------------------------------------------------------------------------------
/config/loader/memory/options.go:
--------------------------------------------------------------------------------
 1 | package memory
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/config/loader"
 5 | 	"go-micro.dev/v5/config/reader"
 6 | 	"go-micro.dev/v5/config/source"
 7 | )
 8 | 
 9 | // WithSource appends a source to list of sources.
10 | func WithSource(s source.Source) loader.Option {
11 | 	return func(o *loader.Options) {
12 | 		o.Source = append(o.Source, s)
13 | 	}
14 | }
15 | 
16 | // WithReader sets the config reader.
17 | func WithReader(r reader.Reader) loader.Option {
18 | 	return func(o *loader.Options) {
19 | 		o.Reader = r
20 | 	}
21 | }
22 | 
23 | func WithWatcherDisabled() loader.Option {
24 | 	return func(o *loader.Options) {
25 | 		o.WithWatcherDisabled = true
26 | 	}
27 | }
28 | 


--------------------------------------------------------------------------------
/config/options.go:
--------------------------------------------------------------------------------
 1 | package config
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/config/loader"
 5 | 	"go-micro.dev/v5/config/reader"
 6 | 	"go-micro.dev/v5/config/source"
 7 | )
 8 | 
 9 | // WithLoader sets the loader for manager config.
10 | func WithLoader(l loader.Loader) Option {
11 | 	return func(o *Options) {
12 | 		o.Loader = l
13 | 	}
14 | }
15 | 
16 | // WithSource appends a source to list of sources.
17 | func WithSource(s source.Source) Option {
18 | 	return func(o *Options) {
19 | 		o.Source = append(o.Source, s)
20 | 	}
21 | }
22 | 
23 | // WithReader sets the config reader.
24 | func WithReader(r reader.Reader) Option {
25 | 	return func(o *Options) {
26 | 		o.Reader = r
27 | 	}
28 | }
29 | 
30 | func WithWatcherDisabled() Option {
31 | 	return func(o *Options) {
32 | 		o.WithWatcherDisabled = true
33 | 	}
34 | }
35 | 


--------------------------------------------------------------------------------
/config/reader/json/json_test.go:
--------------------------------------------------------------------------------
 1 | package json
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"go-micro.dev/v5/config/source"
 7 | )
 8 | 
 9 | func TestReader(t *testing.T) {
10 | 	data := []byte(`{"foo": "bar", "baz": {"bar": "cat"}}`)
11 | 
12 | 	testData := []struct {
13 | 		path  []string
14 | 		value string
15 | 	}{
16 | 		{
17 | 			[]string{"foo"},
18 | 			"bar",
19 | 		},
20 | 		{
21 | 			[]string{"baz", "bar"},
22 | 			"cat",
23 | 		},
24 | 	}
25 | 
26 | 	r := NewReader()
27 | 
28 | 	c, err := r.Merge(&source.ChangeSet{Data: data}, &source.ChangeSet{})
29 | 	if err != nil {
30 | 		t.Fatal(err)
31 | 	}
32 | 
33 | 	values, err := r.Values(c)
34 | 	if err != nil {
35 | 		t.Fatal(err)
36 | 	}
37 | 
38 | 	for _, test := range testData {
39 | 		if v, err := values.Get(test.path...); err != nil {
40 | 			t.Fatal(err)
41 | 		} else if v.String("") != test.value {
42 | 			t.Fatalf("Expected %s got %s for path %v", test.value, v, test.path)
43 | 		}
44 | 	}
45 | }
46 | 


--------------------------------------------------------------------------------
/config/reader/options.go:
--------------------------------------------------------------------------------
 1 | package reader
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/config/encoder"
 5 | 	"go-micro.dev/v5/config/encoder/json"
 6 | )
 7 | 
 8 | type Options struct {
 9 | 	Encoding map[string]encoder.Encoder
10 | }
11 | 
12 | type Option func(o *Options)
13 | 
14 | func NewOptions(opts ...Option) Options {
15 | 	options := Options{
16 | 		Encoding: map[string]encoder.Encoder{
17 | 			"json": json.NewEncoder(),
18 | 		},
19 | 	}
20 | 	for _, o := range opts {
21 | 		o(&options)
22 | 	}
23 | 	return options
24 | }
25 | 
26 | func WithEncoder(e encoder.Encoder) Option {
27 | 	return func(o *Options) {
28 | 		if o.Encoding == nil {
29 | 			o.Encoding = make(map[string]encoder.Encoder)
30 | 		}
31 | 		o.Encoding[e.String()] = e
32 | 	}
33 | }
34 | 


--------------------------------------------------------------------------------
/config/reader/preprocessor.go:
--------------------------------------------------------------------------------
 1 | package reader
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"regexp"
 6 | )
 7 | 
 8 | func ReplaceEnvVars(raw []byte) ([]byte, error) {
 9 | 	re := regexp.MustCompile(`\$\{([A-Za-z0-9_]+)\}`)
10 | 	if re.Match(raw) {
11 | 		dataS := string(raw)
12 | 		res := re.ReplaceAllStringFunc(dataS, replaceEnvVars)
13 | 		return []byte(res), nil
14 | 	} else {
15 | 		return raw, nil
16 | 	}
17 | }
18 | 
19 | func replaceEnvVars(element string) string {
20 | 	v := element[2 : len(element)-1]
21 | 	el := os.Getenv(v)
22 | 	return el
23 | }
24 | 


--------------------------------------------------------------------------------
/config/reader/reader.go:
--------------------------------------------------------------------------------
 1 | // Package reader parses change sets and provides config values
 2 | package reader
 3 | 
 4 | import (
 5 | 	"time"
 6 | 
 7 | 	"go-micro.dev/v5/config/source"
 8 | )
 9 | 
10 | // Reader is an interface for merging changesets.
11 | type Reader interface {
12 | 	Merge(...*source.ChangeSet) (*source.ChangeSet, error)
13 | 	Values(*source.ChangeSet) (Values, error)
14 | 	String() string
15 | }
16 | 
17 | // Values is returned by the reader.
18 | type Values interface {
19 | 	Bytes() []byte
20 | 	Get(path ...string) (Value, error)
21 | 	Set(val interface{}, path ...string)
22 | 	Del(path ...string)
23 | 	Map() map[string]interface{}
24 | 	Scan(v interface{}) error
25 | }
26 | 
27 | // Value represents a value of any type.
28 | type Value interface {
29 | 	Bool(def bool) bool
30 | 	Int(def int) int
31 | 	String(def string) string
32 | 	Float64(def float64) float64
33 | 	Duration(def time.Duration) time.Duration
34 | 	StringSlice(def []string) []string
35 | 	StringMap(def map[string]string) map[string]string
36 | 	Scan(val interface{}) error
37 | 	Bytes() []byte
38 | }
39 | 


--------------------------------------------------------------------------------
/config/secrets/secretbox/secretbox_test.go:
--------------------------------------------------------------------------------
 1 | package secretbox
 2 | 
 3 | import (
 4 | 	"encoding/base64"
 5 | 	"reflect"
 6 | 	"testing"
 7 | 
 8 | 	"go-micro.dev/v5/config/secrets"
 9 | )
10 | 
11 | func TestSecretBox(t *testing.T) {
12 | 	secretKey, err := base64.StdEncoding.DecodeString("4jbVgq8FsAV7vy+n8WqEZrl7BUtNqh3fYT5RXzXOPFY=")
13 | 	if err != nil {
14 | 		t.Fatal(err)
15 | 	}
16 | 
17 | 	s := NewSecrets()
18 | 
19 | 	if err := s.Init(); err == nil {
20 | 		t.Error("Secretbox accepted an empty secret key")
21 | 	}
22 | 	if err := s.Init(secrets.Key([]byte("invalid"))); err == nil {
23 | 		t.Error("Secretbox accepted a secret key that is invalid")
24 | 	}
25 | 
26 | 	if err := s.Init(secrets.Key(secretKey)); err != nil {
27 | 		t.Fatal(err)
28 | 	}
29 | 
30 | 	o := s.Options()
31 | 	if !reflect.DeepEqual(o.Key, secretKey) {
32 | 		t.Error("Init() didn't set secret key correctly")
33 | 	}
34 | 	if s.String() != "nacl-secretbox" {
35 | 		t.Error(s.String() + " should be nacl-secretbox")
36 | 	}
37 | 
38 | 	// Try 10 times to get different nonces
39 | 	for i := 0; i < 10; i++ {
40 | 		message := []byte(`Can you hear me, Major Tom?`)
41 | 
42 | 		encrypted, err := s.Encrypt(message)
43 | 		if err != nil {
44 | 			t.Errorf("Failed to encrypt message (%s)", err)
45 | 		}
46 | 
47 | 		decrypted, err := s.Decrypt(encrypted)
48 | 		if err != nil {
49 | 			t.Errorf("Failed to decrypt encrypted message (%s)", err)
50 | 		}
51 | 
52 | 		if !reflect.DeepEqual(message, decrypted) {
53 | 			t.Errorf("Decrypted Message dod not match encrypted message")
54 | 		}
55 | 	}
56 | }
57 | 


--------------------------------------------------------------------------------
/config/source/changeset.go:
--------------------------------------------------------------------------------
 1 | package source
 2 | 
 3 | import (
 4 | 	"crypto/md5"
 5 | 	"fmt"
 6 | )
 7 | 
 8 | // Sum returns the md5 checksum of the ChangeSet data.
 9 | func (c *ChangeSet) Sum() string {
10 | 	h := md5.New()
11 | 	h.Write(c.Data)
12 | 	return fmt.Sprintf("%x", h.Sum(nil))
13 | }
14 | 


--------------------------------------------------------------------------------
/config/source/cli/README.md:
--------------------------------------------------------------------------------
 1 | # cli Source
 2 | 
 3 | The cli source reads config from parsed flags via a cli.Context.
 4 | 
 5 | ## Format
 6 | 
 7 | We expect the use of the `urfave/cli` package. Upper case flags will be lower cased. Dashes will be used as delimiters for nesting.
 8 | 
 9 | ### Example
10 | 
11 | ```go
12 | micro.Flags(
13 |     cli.StringFlag{
14 |         Name: "database-address",
15 |         Value: "127.0.0.1",
16 |         Usage: "the db address",
17 |     },
18 |     cli.IntFlag{
19 |         Name: "database-port",
20 |         Value: 3306,
21 |         Usage: "the db port",
22 |     },
23 | )
24 | ```
25 | 
26 | Becomes
27 | 
28 | ```json
29 | {
30 |   "database": {
31 |     "address": "127.0.0.1",
32 |     "port": 3306
33 |   }
34 | }
35 | ```
36 | 
37 | ## New and Load Source
38 | 
39 | Because a cli.Context is needed to retrieve the flags and their values, it is recommended to build your source from within a cli.Action.
40 | 
41 | ```go
42 | 
43 | func main() {
44 |     // New Service
45 |     service := micro.NewService(
46 |         micro.Name("example"),
47 |         micro.Flags(
48 |             cli.StringFlag{
49 |                 Name: "database-address",
50 |                 Value: "127.0.0.1",
51 |                 Usage: "the db address",
52 |             },
53 |         ),
54 |     )
55 | 
56 |     var clisrc source.Source
57 | 
58 |     service.Init(
59 |         micro.Action(func(c *cli.Context) {
60 |             clisrc = cli.NewSource(
61 |                 cli.Context(c),
62 | 	    )
63 |             // Alternatively, just setup your config right here
64 |         }),
65 |     )
66 | 
67 |     // ... Load and use that source ...
68 |     conf := config.NewConfig()
69 |     conf.Load(clisrc)
70 | }
71 | ```
72 | 


--------------------------------------------------------------------------------
/config/source/cli/options.go:
--------------------------------------------------------------------------------
 1 | package cli
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"github.com/urfave/cli/v2"
 7 | 	"go-micro.dev/v5/config/source"
 8 | )
 9 | 
10 | type contextKey struct{}
11 | 
12 | // Context sets the cli context.
13 | func Context(c *cli.Context) source.Option {
14 | 	return func(o *source.Options) {
15 | 		if o.Context == nil {
16 | 			o.Context = context.Background()
17 | 		}
18 | 		o.Context = context.WithValue(o.Context, contextKey{}, c)
19 | 	}
20 | }
21 | 


--------------------------------------------------------------------------------
/config/source/cli/util.go:
--------------------------------------------------------------------------------
 1 | package cli
 2 | 
 3 | import (
 4 | 	"errors"
 5 | 	"flag"
 6 | 	"strings"
 7 | 
 8 | 	"github.com/urfave/cli/v2"
 9 | )
10 | 
11 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
12 | 	switch ff.Value.(type) {
13 | 	case *cli.StringSlice:
14 | 	default:
15 | 		set.Set(name, ff.Value.String())
16 | 	}
17 | }
18 | 
19 | func normalizeFlags(flags []cli.Flag, set *flag.FlagSet) error {
20 | 	visited := make(map[string]bool)
21 | 	set.Visit(func(f *flag.Flag) {
22 | 		visited[f.Name] = true
23 | 	})
24 | 	for _, f := range flags {
25 | 		parts := f.Names()
26 | 		if len(parts) == 1 {
27 | 			continue
28 | 		}
29 | 		var ff *flag.Flag
30 | 		for _, name := range parts {
31 | 			name = strings.Trim(name, " ")
32 | 			if visited[name] {
33 | 				if ff != nil {
34 | 					return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
35 | 				}
36 | 				ff = set.Lookup(name)
37 | 			}
38 | 		}
39 | 		if ff == nil {
40 | 			continue
41 | 		}
42 | 		for _, name := range parts {
43 | 			name = strings.Trim(name, " ")
44 | 			if !visited[name] {
45 | 				copyFlag(name, ff, set)
46 | 			}
47 | 		}
48 | 	}
49 | 	return nil
50 | }
51 | 


--------------------------------------------------------------------------------
/config/source/env/options.go:
--------------------------------------------------------------------------------
 1 | package env
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"strings"
 6 | 
 7 | 	"go-micro.dev/v5/config/source"
 8 | )
 9 | 
10 | type strippedPrefixKey struct{}
11 | type prefixKey struct{}
12 | 
13 | // WithStrippedPrefix sets the environment variable prefixes to scope to.
14 | // These prefixes will be removed from the actual config entries.
15 | func WithStrippedPrefix(p ...string) source.Option {
16 | 	return func(o *source.Options) {
17 | 		if o.Context == nil {
18 | 			o.Context = context.Background()
19 | 		}
20 | 
21 | 		o.Context = context.WithValue(o.Context, strippedPrefixKey{}, appendUnderscore(p))
22 | 	}
23 | }
24 | 
25 | // WithPrefix sets the environment variable prefixes to scope to.
26 | // These prefixes will not be removed. Each prefix will be considered a top level config entry.
27 | func WithPrefix(p ...string) source.Option {
28 | 	return func(o *source.Options) {
29 | 		if o.Context == nil {
30 | 			o.Context = context.Background()
31 | 		}
32 | 		o.Context = context.WithValue(o.Context, prefixKey{}, appendUnderscore(p))
33 | 	}
34 | }
35 | 
36 | func appendUnderscore(prefixes []string) []string {
37 | 	//nolint:prealloc
38 | 	var result []string
39 | 	for _, p := range prefixes {
40 | 		if !strings.HasSuffix(p, "_") {
41 | 			result = append(result, p+"_")
42 | 			continue
43 | 		}
44 | 
45 | 		result = append(result, p)
46 | 	}
47 | 
48 | 	return result
49 | }
50 | 


--------------------------------------------------------------------------------
/config/source/env/watcher.go:
--------------------------------------------------------------------------------
 1 | package env
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/config/source"
 5 | )
 6 | 
 7 | type watcher struct {
 8 | 	exit chan struct{}
 9 | }
10 | 
11 | func (w *watcher) Next() (*source.ChangeSet, error) {
12 | 	<-w.exit
13 | 
14 | 	return nil, source.ErrWatcherStopped
15 | }
16 | 
17 | func (w *watcher) Stop() error {
18 | 	close(w.exit)
19 | 	return nil
20 | }
21 | 
22 | func newWatcher() (source.Watcher, error) {
23 | 	return &watcher{exit: make(chan struct{})}, nil
24 | }
25 | 


--------------------------------------------------------------------------------
/config/source/file/README.md:
--------------------------------------------------------------------------------
 1 | # File Source
 2 | 
 3 | The file source reads config from a file.
 4 | 
 5 | It uses the File extension to determine the Format e.g `config.yaml` has the yaml format.
 6 | It does not make use of encoders or interpet the file data. If a file extension is not present
 7 | the source Format will default to the Encoder in options.
 8 | 
 9 | ## Example
10 | 
11 | A config file format in json
12 | 
13 | ```json
14 | {
15 |   "hosts": {
16 |     "database": {
17 |       "address": "10.0.0.1",
18 |       "port": 3306
19 |     },
20 |     "cache": {
21 |       "address": "10.0.0.2",
22 |       "port": 6379
23 |     }
24 |   }
25 | }
26 | ```
27 | 
28 | ## New Source
29 | 
30 | Specify file source with path to file. Path is optional and will default to `config.json`
31 | 
32 | ```go
33 | fileSource := file.NewSource(
34 | 	file.WithPath("/tmp/config.json"),
35 | )
36 | ```
37 | 
38 | ## File Format
39 | 
40 | To load different file formats e.g yaml, toml, xml simply specify them with their extension
41 | 
42 | ```go
43 | fileSource := file.NewSource(
44 |         file.WithPath("/tmp/config.yaml"),
45 | )
46 | ```
47 | 
48 | If you want to specify a file without extension, ensure you set the encoder to the same format
49 | 
50 | ```go
51 | e := toml.NewEncoder()
52 | 
53 | fileSource := file.NewSource(
54 |         file.WithPath("/tmp/config"),
55 |         source.WithEncoder(e),
56 | )
57 | ```
58 | 
59 | ## Load Source
60 | 
61 | Load the source into config
62 | 
63 | ```go
64 | // Create new config
65 | conf := config.NewConfig()
66 | 
67 | // Load file source
68 | conf.Load(fileSource)
69 | ```
70 | 


--------------------------------------------------------------------------------
/config/source/file/format.go:
--------------------------------------------------------------------------------
 1 | package file
 2 | 
 3 | import (
 4 | 	"strings"
 5 | 
 6 | 	"go-micro.dev/v5/config/encoder"
 7 | )
 8 | 
 9 | func format(p string, e encoder.Encoder) string {
10 | 	parts := strings.Split(p, ".")
11 | 	if len(parts) > 1 {
12 | 		return parts[len(parts)-1]
13 | 	}
14 | 	return e.String()
15 | }
16 | 


--------------------------------------------------------------------------------
/config/source/file/format_test.go:
--------------------------------------------------------------------------------
 1 | package file
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"go-micro.dev/v5/config/source"
 7 | )
 8 | 
 9 | func TestFormat(t *testing.T) {
10 | 	opts := source.NewOptions()
11 | 	e := opts.Encoder
12 | 
13 | 	testCases := []struct {
14 | 		p string
15 | 		f string
16 | 	}{
17 | 		{"/foo/bar.json", "json"},
18 | 		{"/foo/bar.yaml", "yaml"},
19 | 		{"/foo/bar.xml", "xml"},
20 | 		{"/foo/bar.conf.ini", "ini"},
21 | 		{"conf", e.String()},
22 | 	}
23 | 
24 | 	for _, d := range testCases {
25 | 		f := format(d.p, e)
26 | 		if f != d.f {
27 | 			t.Fatalf("%s: expected %s got %s", d.p, d.f, f)
28 | 		}
29 | 	}
30 | }
31 | 


--------------------------------------------------------------------------------
/config/source/file/options.go:
--------------------------------------------------------------------------------
 1 | package file
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"io/fs"
 6 | 
 7 | 	"go-micro.dev/v5/config/source"
 8 | )
 9 | 
10 | type filePathKey struct{}
11 | type fsKey struct{}
12 | 
13 | // WithPath sets the path to file.
14 | func WithPath(p string) source.Option {
15 | 	return func(o *source.Options) {
16 | 		if o.Context == nil {
17 | 			o.Context = context.Background()
18 | 		}
19 | 		o.Context = context.WithValue(o.Context, filePathKey{}, p)
20 | 	}
21 | }
22 | 
23 | // WithFS sets the underlying filesystem to lookup file from  (default os.FS).
24 | func WithFS(fs fs.FS) source.Option {
25 | 	return func(o *source.Options) {
26 | 		if o.Context == nil {
27 | 			o.Context = context.Background()
28 | 		}
29 | 		o.Context = context.WithValue(o.Context, fsKey{}, fs)
30 | 	}
31 | }
32 | 


--------------------------------------------------------------------------------
/config/source/file/watcher.go:
--------------------------------------------------------------------------------
 1 | //go:build !linux
 2 | // +build !linux
 3 | 
 4 | package file
 5 | 
 6 | import (
 7 | 	"os"
 8 | 
 9 | 	"github.com/fsnotify/fsnotify"
10 | 	"go-micro.dev/v5/config/source"
11 | )
12 | 
13 | type watcher struct {
14 | 	f *file
15 | 
16 | 	fw *fsnotify.Watcher
17 | }
18 | 
19 | func newWatcher(f *file) (source.Watcher, error) {
20 | 	fw, err := fsnotify.NewWatcher()
21 | 	if err != nil {
22 | 		return nil, err
23 | 	}
24 | 
25 | 	fw.Add(f.path)
26 | 
27 | 	return &watcher{
28 | 		f:  f,
29 | 		fw: fw,
30 | 	}, nil
31 | }
32 | 
33 | func (w *watcher) Next() (*source.ChangeSet, error) {
34 | 	// try get the event
35 | 	select {
36 | 	case event, ok := <-w.fw.Events:
37 | 		// check if channel was closed (i.e. Watcher.Close() was called).
38 | 		if !ok {
39 | 			return nil, source.ErrWatcherStopped
40 | 		}
41 | 
42 | 		if event.Has(fsnotify.Rename) {
43 | 			// check existence of file, and add watch again
44 | 			_, err := os.Stat(event.Name)
45 | 			if err == nil || os.IsExist(err) {
46 | 				w.fw.Add(event.Name)
47 | 			}
48 | 		}
49 | 
50 | 		c, err := w.f.Read()
51 | 		if err != nil {
52 | 			return nil, err
53 | 		}
54 | 
55 | 		return c, nil
56 | 	case err, ok := <-w.fw.Errors:
57 | 		// check if channel was closed (i.e. Watcher.Close() was called).
58 | 		if !ok {
59 | 			return nil, source.ErrWatcherStopped
60 | 		}
61 | 
62 | 		return nil, err
63 | 	}
64 | }
65 | 
66 | func (w *watcher) Stop() error {
67 | 	return w.fw.Close()
68 | }
69 | 


--------------------------------------------------------------------------------
/config/source/file/watcher_linux.go:
--------------------------------------------------------------------------------
 1 | //go:build linux
 2 | // +build linux
 3 | 
 4 | package file
 5 | 
 6 | import (
 7 | 	"os"
 8 | 
 9 | 	"github.com/fsnotify/fsnotify"
10 | 	"go-micro.dev/v5/config/source"
11 | )
12 | 
13 | type watcher struct {
14 | 	f *file
15 | 
16 | 	fw *fsnotify.Watcher
17 | }
18 | 
19 | func newWatcher(f *file) (source.Watcher, error) {
20 | 	fw, err := fsnotify.NewWatcher()
21 | 	if err != nil {
22 | 		return nil, err
23 | 	}
24 | 
25 | 	fw.Add(f.path)
26 | 
27 | 	return &watcher{
28 | 		f:  f,
29 | 		fw: fw,
30 | 	}, nil
31 | }
32 | 
33 | func (w *watcher) Next() (*source.ChangeSet, error) {
34 | 	// try get the event
35 | 	select {
36 | 	case event, ok := <-w.fw.Events:
37 | 		// check if channel was closed (i.e. Watcher.Close() was called).
38 | 		if !ok {
39 | 			return nil, source.ErrWatcherStopped
40 | 		}
41 | 
42 | 		if event.Has(fsnotify.Rename) {
43 | 			// check existence of file, and add watch again
44 | 			_, err := os.Stat(event.Name)
45 | 			if err == nil || os.IsExist(err) {
46 | 				w.fw.Add(event.Name)
47 | 			}
48 | 		}
49 | 
50 | 		c, err := w.f.Read()
51 | 		if err != nil {
52 | 			return nil, err
53 | 		}
54 | 
55 | 		// add path again for the event bug of fsnotify
56 | 		w.fw.Add(w.f.path)
57 | 
58 | 		return c, nil
59 | 	case err, ok := <-w.fw.Errors:
60 | 		// check if channel was closed (i.e. Watcher.Close() was called).
61 | 		if !ok {
62 | 			return nil, source.ErrWatcherStopped
63 | 		}
64 | 
65 | 		return nil, err
66 | 	}
67 | }
68 | 
69 | func (w *watcher) Stop() error {
70 | 	return w.fw.Close()
71 | }
72 | 


--------------------------------------------------------------------------------
/config/source/flag/README.md:
--------------------------------------------------------------------------------
 1 | # Flag Source
 2 | 
 3 | The flag source reads config from flags
 4 | 
 5 | ## Format
 6 | 
 7 | We expect the use of the `flag` package. Upper case flags will be lower cased. Dashes will be used as delimiters.
 8 | 
 9 | ### Example
10 | 
11 | ```go
12 | dbAddress := flag.String("database_address", "127.0.0.1", "the db address")
13 | dbPort := flag.Int("database_port", 3306, "the db port)
14 | ```
15 | 
16 | Becomes
17 | 
18 | ```json
19 | {
20 |   "database": {
21 |     "address": "127.0.0.1",
22 |     "port": 3306
23 |   }
24 | }
25 | ```
26 | 
27 | ## New Source
28 | 
29 | ```go
30 | flagSource := flag.NewSource(
31 | 	// optionally enable reading of unset flags and their default
32 | 	// values into config, defaults to false
33 | 	IncludeUnset(true)
34 | )
35 | ```
36 | 
37 | ## Load Source
38 | 
39 | Load the source into config
40 | 
41 | ```go
42 | // Create new config
43 | conf := config.NewConfig()
44 | 
45 | // Load flag source
46 | conf.Load(flagSource)
47 | ```
48 | 


--------------------------------------------------------------------------------
/config/source/flag/flag_test.go:
--------------------------------------------------------------------------------
 1 | package flag
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"flag"
 6 | 	"testing"
 7 | )
 8 | 
 9 | var (
10 | 	dbuser = flag.String("database-user", "default", "db user")
11 | 	dbhost = flag.String("database-host", "", "db host")
12 | 	dbpw   = flag.String("database-password", "", "db pw")
13 | )
14 | 
15 | func initTestFlags() {
16 | 	flag.Set("database-host", "localhost")
17 | 	flag.Set("database-password", "some-password")
18 | 	flag.Parse()
19 | }
20 | 
21 | func TestFlagsrc_Read(t *testing.T) {
22 | 	initTestFlags()
23 | 	source := NewSource()
24 | 	c, err := source.Read()
25 | 	if err != nil {
26 | 		t.Error(err)
27 | 	}
28 | 
29 | 	var actual map[string]interface{}
30 | 	if err := json.Unmarshal(c.Data, &actual); err != nil {
31 | 		t.Error(err)
32 | 	}
33 | 
34 | 	actualDB := actual["database"].(map[string]interface{})
35 | 	if actualDB["host"] != *dbhost {
36 | 		t.Errorf("expected %v got %v", *dbhost, actualDB["host"])
37 | 	}
38 | 
39 | 	if actualDB["password"] != *dbpw {
40 | 		t.Errorf("expected %v got %v", *dbpw, actualDB["password"])
41 | 	}
42 | 
43 | 	// unset flags should not be loaded
44 | 	if actualDB["user"] != nil {
45 | 		t.Errorf("expected %v got %v", nil, actualDB["user"])
46 | 	}
47 | }
48 | 
49 | func TestFlagsrc_ReadAll(t *testing.T) {
50 | 	initTestFlags()
51 | 	source := NewSource(IncludeUnset(true))
52 | 	c, err := source.Read()
53 | 	if err != nil {
54 | 		t.Error(err)
55 | 	}
56 | 
57 | 	var actual map[string]interface{}
58 | 	if err := json.Unmarshal(c.Data, &actual); err != nil {
59 | 		t.Error(err)
60 | 	}
61 | 
62 | 	actualDB := actual["database"].(map[string]interface{})
63 | 
64 | 	// unset flag defaults should be loaded
65 | 	if actualDB["user"] != *dbuser {
66 | 		t.Errorf("expected %v got %v", *dbuser, actualDB["user"])
67 | 	}
68 | }
69 | 


--------------------------------------------------------------------------------
/config/source/flag/options.go:
--------------------------------------------------------------------------------
 1 | package flag
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/config/source"
 7 | )
 8 | 
 9 | type includeUnsetKey struct{}
10 | 
11 | // IncludeUnset toggles the loading of unset flags and their respective default values.
12 | // Default behavior is to ignore any unset flags.
13 | func IncludeUnset(b bool) source.Option {
14 | 	return func(o *source.Options) {
15 | 		if o.Context == nil {
16 | 			o.Context = context.Background()
17 | 		}
18 | 		o.Context = context.WithValue(o.Context, includeUnsetKey{}, true)
19 | 	}
20 | }
21 | 


--------------------------------------------------------------------------------
/config/source/memory/README.md:
--------------------------------------------------------------------------------
 1 | # Memory Source
 2 | 
 3 | The memory source provides in-memory data as a source
 4 | 
 5 | ## Memory Format
 6 | 
 7 | The expected data format is json
 8 | 
 9 | ```json
10 | data := []byte(`{
11 |     "hosts": {
12 |         "database": {
13 |             "address": "10.0.0.1",
14 |             "port": 3306
15 |         },
16 |         "cache": {
17 |             "address": "10.0.0.2",
18 |             "port": 6379
19 |         }
20 |     }
21 | }`)
22 | ```
23 | 
24 | ## New Source
25 | 
26 | Specify source with data
27 | 
28 | ```go
29 | memorySource := memory.NewSource(
30 | 	memory.WithJSON(data),
31 | )
32 | ```
33 | 
34 | ## Load Source
35 | 
36 | Load the source into config
37 | 
38 | ```go
39 | // Create new config
40 | conf := config.NewConfig()
41 | 
42 | // Load memory source
43 | conf.Load(memorySource)
44 | ```
45 | 


--------------------------------------------------------------------------------
/config/source/memory/options.go:
--------------------------------------------------------------------------------
 1 | package memory
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/config/source"
 7 | )
 8 | 
 9 | type changeSetKey struct{}
10 | 
11 | func withData(d []byte, f string) source.Option {
12 | 	return func(o *source.Options) {
13 | 		if o.Context == nil {
14 | 			o.Context = context.Background()
15 | 		}
16 | 		o.Context = context.WithValue(o.Context, changeSetKey{}, &source.ChangeSet{
17 | 			Data:   d,
18 | 			Format: f,
19 | 		})
20 | 	}
21 | }
22 | 
23 | // WithChangeSet allows a changeset to be set.
24 | func WithChangeSet(cs *source.ChangeSet) source.Option {
25 | 	return func(o *source.Options) {
26 | 		if o.Context == nil {
27 | 			o.Context = context.Background()
28 | 		}
29 | 		o.Context = context.WithValue(o.Context, changeSetKey{}, cs)
30 | 	}
31 | }
32 | 
33 | // WithJSON allows the source data to be set to json.
34 | func WithJSON(d []byte) source.Option {
35 | 	return withData(d, "json")
36 | }
37 | 
38 | // WithYAML allows the source data to be set to yaml.
39 | func WithYAML(d []byte) source.Option {
40 | 	return withData(d, "yaml")
41 | }
42 | 


--------------------------------------------------------------------------------
/config/source/memory/watcher.go:
--------------------------------------------------------------------------------
 1 | package memory
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/config/source"
 5 | )
 6 | 
 7 | type watcher struct {
 8 | 	Updates chan *source.ChangeSet
 9 | 	Source  *memory
10 | 	Id      string
11 | }
12 | 
13 | func (w *watcher) Next() (*source.ChangeSet, error) {
14 | 	cs := <-w.Updates
15 | 	return cs, nil
16 | }
17 | 
18 | func (w *watcher) Stop() error {
19 | 	w.Source.Lock()
20 | 	delete(w.Source.Watchers, w.Id)
21 | 	w.Source.Unlock()
22 | 	return nil
23 | }
24 | 


--------------------------------------------------------------------------------
/config/source/nats/README.md:
--------------------------------------------------------------------------------
 1 | # Nats Source
 2 | 
 3 | The nats source reads config from nats key/values
 4 | 
 5 | ## Nats Format
 6 | 
 7 | The nats source expects keys under the default bucket `default` default key `micro_config`
 8 | 
 9 | Values are expected to be json
10 | 
11 | ```
12 | nats kv put default micro_config '{"nats": {"address": "10.0.0.1", "port": 8488}}'
13 | ```
14 | 
15 | ```
16 | conf.Get("nats")
17 | ```
18 | 
19 | ## New Source
20 | 
21 | Specify source with data
22 | 
23 | ```go
24 | natsSource := nats.NewSource(
25 | 	nats.WithUrl("127.0.0.1:4222"),
26 | 	nats.WithBucket("my_bucket"),
27 | 	nats.WithKey("my_key"),
28 | )
29 | ```
30 | 
31 | ## Load Source
32 | 
33 | Load the source into config
34 | 
35 | ```go
36 | // Create new config
37 | conf := config.NewConfig()
38 | 
39 | // Load nats source
40 | conf.Load(natsSource)
41 | ```
42 | 
43 | ## Watch
44 | 
45 | ```go
46 | wh, _ := natsSource.Watch()
47 | 
48 | for {
49 | 	v, err := watcher.Next()
50 | 	if err != nil {
51 | 		log.Fatalf("err %v", err)
52 | 	}
53 | 
54 | 	log.Infof("data %v", string(v.Data))
55 | }
56 | ```
57 | 


--------------------------------------------------------------------------------
/config/source/nats/options.go:
--------------------------------------------------------------------------------
 1 | package nats
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"time"
 6 | 
 7 | 	natsgo "github.com/nats-io/nats.go"
 8 | 	"go-micro.dev/v5/config/source"
 9 | )
10 | 
11 | type (
12 | 	urlKey    struct{}
13 | 	bucketKey struct{}
14 | 	keyKey    struct{}
15 | )
16 | 
17 | // WithUrl sets the nats url.
18 | func WithUrl(a ...string) source.Option {
19 | 	return func(o *source.Options) {
20 | 		if o.Context == nil {
21 | 			o.Context = context.Background()
22 | 		}
23 | 		o.Context = context.WithValue(o.Context, urlKey{}, a)
24 | 	}
25 | }
26 | 
27 | // WithBucket sets the nats key.
28 | func WithBucket(a string) source.Option {
29 | 	return func(o *source.Options) {
30 | 		if o.Context == nil {
31 | 			o.Context = context.Background()
32 | 		}
33 | 		o.Context = context.WithValue(o.Context, bucketKey{}, a)
34 | 	}
35 | }
36 | 
37 | // WithKey sets the nats key.
38 | func WithKey(a string) source.Option {
39 | 	return func(o *source.Options) {
40 | 		if o.Context == nil {
41 | 			o.Context = context.Background()
42 | 		}
43 | 		o.Context = context.WithValue(o.Context, keyKey{}, a)
44 | 	}
45 | }
46 | 
47 | func Client(url string) (natsgo.JetStreamContext, error) {
48 | 	nc, err := natsgo.Connect(url)
49 | 	if err != nil {
50 | 		return nil, err
51 | 	}
52 | 
53 | 	return nc.JetStream(natsgo.MaxWait(10 * time.Second))
54 | }
55 | 


--------------------------------------------------------------------------------
/config/source/nats/watcher.go:
--------------------------------------------------------------------------------
 1 | package nats
 2 | 
 3 | import (
 4 | 	"time"
 5 | 
 6 | 	natsgo "github.com/nats-io/nats.go"
 7 | 	"go-micro.dev/v5/config/encoder"
 8 | 	"go-micro.dev/v5/config/source"
 9 | )
10 | 
11 | type watcher struct {
12 | 	e      encoder.Encoder
13 | 	name   string
14 | 	bucket string
15 | 	key    string
16 | 
17 | 	ch   chan *source.ChangeSet
18 | 	exit chan bool
19 | }
20 | 
21 | func newWatcher(kv natsgo.KeyValue, bucket, key, name string, e encoder.Encoder) (source.Watcher, error) {
22 | 	w := &watcher{
23 | 		e:      e,
24 | 		name:   name,
25 | 		bucket: bucket,
26 | 		key:    key,
27 | 		ch:     make(chan *source.ChangeSet),
28 | 		exit:   make(chan bool),
29 | 	}
30 | 
31 | 	wh, _ := kv.Watch(key)
32 | 
33 | 	go func() {
34 | 		for {
35 | 			select {
36 | 			case v := <-wh.Updates():
37 | 				if v != nil {
38 | 					w.handle(v.Value())
39 | 				}
40 | 			case <-w.exit:
41 | 				_ = wh.Stop()
42 | 				return
43 | 			}
44 | 		}
45 | 	}()
46 | 	return w, nil
47 | }
48 | 
49 | func (w *watcher) handle(data []byte) {
50 | 	cs := &source.ChangeSet{
51 | 		Timestamp: time.Now(),
52 | 		Format:    w.e.String(),
53 | 		Source:    w.name,
54 | 		Data:      data,
55 | 	}
56 | 	cs.Checksum = cs.Sum()
57 | 
58 | 	w.ch <- cs
59 | }
60 | 
61 | func (w *watcher) Next() (*source.ChangeSet, error) {
62 | 	select {
63 | 	case cs := <-w.ch:
64 | 		return cs, nil
65 | 	case <-w.exit:
66 | 		return nil, source.ErrWatcherStopped
67 | 	}
68 | }
69 | 
70 | func (w *watcher) Stop() error {
71 | 	select {
72 | 	case <-w.exit:
73 | 		return nil
74 | 	default:
75 | 		close(w.exit)
76 | 	}
77 | 
78 | 	return nil
79 | }
80 | 


--------------------------------------------------------------------------------
/config/source/noop.go:
--------------------------------------------------------------------------------
 1 | package source
 2 | 
 3 | import (
 4 | 	"errors"
 5 | )
 6 | 
 7 | type noopWatcher struct {
 8 | 	exit chan struct{}
 9 | }
10 | 
11 | func (w *noopWatcher) Next() (*ChangeSet, error) {
12 | 	<-w.exit
13 | 
14 | 	return nil, errors.New("noopWatcher stopped")
15 | }
16 | 
17 | func (w *noopWatcher) Stop() error {
18 | 	close(w.exit)
19 | 	return nil
20 | }
21 | 
22 | // NewNoopWatcher returns a watcher that blocks on Next() until Stop() is called.
23 | func NewNoopWatcher() (Watcher, error) {
24 | 	return &noopWatcher{exit: make(chan struct{})}, nil
25 | }
26 | 


--------------------------------------------------------------------------------
/config/source/options.go:
--------------------------------------------------------------------------------
 1 | package source
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/client"
 7 | 	"go-micro.dev/v5/config/encoder"
 8 | 	"go-micro.dev/v5/config/encoder/json"
 9 | )
10 | 
11 | type Options struct {
12 | 	// Encoder
13 | 	Encoder encoder.Encoder
14 | 
15 | 	// for alternative data
16 | 	Context context.Context
17 | 
18 | 	// Client to use for RPC
19 | 	Client client.Client
20 | }
21 | 
22 | type Option func(o *Options)
23 | 
24 | func NewOptions(opts ...Option) Options {
25 | 	options := Options{
26 | 		Encoder: json.NewEncoder(),
27 | 		Context: context.Background(),
28 | 		Client:  client.DefaultClient,
29 | 	}
30 | 
31 | 	for _, o := range opts {
32 | 		o(&options)
33 | 	}
34 | 
35 | 	return options
36 | }
37 | 
38 | // WithEncoder sets the source encoder.
39 | func WithEncoder(e encoder.Encoder) Option {
40 | 	return func(o *Options) {
41 | 		o.Encoder = e
42 | 	}
43 | }
44 | 
45 | // WithClient sets the source client.
46 | func WithClient(c client.Client) Option {
47 | 	return func(o *Options) {
48 | 		o.Client = c
49 | 	}
50 | }
51 | 


--------------------------------------------------------------------------------
/config/source/source.go:
--------------------------------------------------------------------------------
 1 | // Package source is the interface for sources
 2 | package source
 3 | 
 4 | import (
 5 | 	"errors"
 6 | 	"time"
 7 | )
 8 | 
 9 | var (
10 | 	// ErrWatcherStopped is returned when source watcher has been stopped.
11 | 	ErrWatcherStopped = errors.New("watcher stopped")
12 | )
13 | 
14 | // Source is the source from which config is loaded.
15 | type Source interface {
16 | 	Read() (*ChangeSet, error)
17 | 	Write(*ChangeSet) error
18 | 	Watch() (Watcher, error)
19 | 	String() string
20 | }
21 | 
22 | // ChangeSet represents a set of changes from a source.
23 | type ChangeSet struct {
24 | 	Timestamp time.Time
25 | 	Checksum  string
26 | 	Format    string
27 | 	Source    string
28 | 	Data      []byte
29 | }
30 | 
31 | // Watcher watches a source for changes.
32 | type Watcher interface {
33 | 	Next() (*ChangeSet, error)
34 | 	Stop() error
35 | }
36 | 


--------------------------------------------------------------------------------
/config/value.go:
--------------------------------------------------------------------------------
 1 | package config
 2 | 
 3 | import (
 4 | 	"time"
 5 | 
 6 | 	"go-micro.dev/v5/config/reader"
 7 | )
 8 | 
 9 | type value struct{}
10 | 
11 | func newValue() reader.Value {
12 | 	return new(value)
13 | }
14 | 
15 | func (v *value) Bool(def bool) bool {
16 | 	return false
17 | }
18 | 
19 | func (v *value) Int(def int) int {
20 | 	return 0
21 | }
22 | 
23 | func (v *value) String(def string) string {
24 | 	return ""
25 | }
26 | 
27 | func (v *value) Float64(def float64) float64 {
28 | 	return 0.0
29 | }
30 | 
31 | func (v *value) Duration(def time.Duration) time.Duration {
32 | 	return time.Duration(0)
33 | }
34 | 
35 | func (v *value) StringSlice(def []string) []string {
36 | 	return nil
37 | }
38 | 
39 | func (v *value) StringMap(def map[string]string) map[string]string {
40 | 	return map[string]string{}
41 | }
42 | 
43 | func (v *value) Scan(val interface{}) error {
44 | 	return nil
45 | }
46 | 
47 | func (v *value) Bytes() []byte {
48 | 	return nil
49 | }
50 | 


--------------------------------------------------------------------------------
/debug/log/log.go:
--------------------------------------------------------------------------------
 1 | // Package log provides debug logging
 2 | package log
 3 | 
 4 | import (
 5 | 	"encoding/json"
 6 | 	"fmt"
 7 | 	"time"
 8 | )
 9 | 
10 | var (
11 | 	// Default buffer size if any.
12 | 	DefaultSize = 1024
13 | 	// DefaultLog logger.
14 | 	DefaultLog = NewLog()
15 | 	// Default formatter.
16 | 	DefaultFormat = TextFormat
17 | )
18 | 
19 | // Log is debug log interface for reading and writing logs.
20 | type Log interface {
21 | 	// Read reads log entries from the logger
22 | 	Read(...ReadOption) ([]Record, error)
23 | 	// Write writes records to log
24 | 	Write(Record) error
25 | 	// Stream log records
26 | 	Stream() (Stream, error)
27 | }
28 | 
29 | // Record is log record entry.
30 | type Record struct {
31 | 	// Timestamp of logged event
32 | 	Timestamp time.Time `json:"timestamp"`
33 | 	// Metadata to enrich log record
34 | 	Metadata map[string]string `json:"metadata"`
35 | 	// Value contains log entry
36 | 	Message interface{} `json:"message"`
37 | }
38 | 
39 | // Stream returns a log stream.
40 | type Stream interface {
41 | 	Chan() <-chan Record
42 | 	Stop() error
43 | }
44 | 
45 | // Format is a function which formats the output.
46 | type FormatFunc func(Record) string
47 | 
48 | // TextFormat returns text format.
49 | func TextFormat(r Record) string {
50 | 	t := r.Timestamp.Format("2006-01-02 15:04:05")
51 | 	return fmt.Sprintf("%s %v", t, r.Message)
52 | }
53 | 
54 | // JSONFormat is a json Format func.
55 | func JSONFormat(r Record) string {
56 | 	b, _ := json.Marshal(r)
57 | 	return string(b)
58 | }
59 | 


--------------------------------------------------------------------------------
/debug/log/memory/memory_test.go:
--------------------------------------------------------------------------------
 1 | package memory
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"go-micro.dev/v5/debug/log"
 8 | )
 9 | 
10 | func TestLogger(t *testing.T) {
11 | 	// set size to some value
12 | 	size := 100
13 | 	// override the global logger
14 | 	lg := NewLog(log.Size(size))
15 | 	// make sure we have the right size of the logger ring buffer
16 | 	if lg.(*memoryLog).Size() != size {
17 | 		t.Errorf("expected buffer size: %d, got: %d", size, lg.(*memoryLog).Size())
18 | 	}
19 | 
20 | 	// Log some cruft
21 | 	lg.Write(log.Record{Message: "foobar"})
22 | 	lg.Write(log.Record{Message: "foo bar"})
23 | 
24 | 	// Check if the logs are stored in the logger ring buffer
25 | 	expected := []string{"foobar", "foo bar"}
26 | 	entries, _ := lg.Read(log.Count(len(expected)))
27 | 	for i, entry := range entries {
28 | 		if !reflect.DeepEqual(entry.Message, expected[i]) {
29 | 			t.Errorf("expected %s, got %s", expected[i], entry.Message)
30 | 		}
31 | 	}
32 | }
33 | 


--------------------------------------------------------------------------------
/debug/log/memory/stream.go:
--------------------------------------------------------------------------------
 1 | package memory
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/debug/log"
 5 | )
 6 | 
 7 | type logStream struct {
 8 | 	stream <-chan log.Record
 9 | 	stop   chan bool
10 | }
11 | 
12 | func (l *logStream) Chan() <-chan log.Record {
13 | 	return l.stream
14 | }
15 | 
16 | func (l *logStream) Stop() error {
17 | 	select {
18 | 	case <-l.stop:
19 | 		return nil
20 | 	default:
21 | 		close(l.stop)
22 | 	}
23 | 	return nil
24 | }
25 | 


--------------------------------------------------------------------------------
/debug/log/noop/noop.go:
--------------------------------------------------------------------------------
 1 | package noop
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/debug/log"
 5 | )
 6 | 
 7 | type noop struct{}
 8 | 
 9 | func (n *noop) Read(...log.ReadOption) ([]log.Record, error) {
10 | 	return nil, nil
11 | }
12 | 
13 | func (n *noop) Write(log.Record) error {
14 | 	return nil
15 | }
16 | 
17 | func (n *noop) Stream() (log.Stream, error) {
18 | 	return nil, nil
19 | }
20 | 
21 | func NewLog(opts ...log.Option) log.Log {
22 | 	return new(noop)
23 | }
24 | 


--------------------------------------------------------------------------------
/debug/log/options.go:
--------------------------------------------------------------------------------
 1 | package log
 2 | 
 3 | import "time"
 4 | 
 5 | // Option used by the logger.
 6 | type Option func(*Options)
 7 | 
 8 | // Options are logger options.
 9 | type Options struct {
10 | 	// Format specifies the output format
11 | 	Format FormatFunc
12 | 	// Name of the log
13 | 	Name string
14 | 	// Size is the size of ring buffer
15 | 	Size int
16 | }
17 | 
18 | // Name of the log.
19 | func Name(n string) Option {
20 | 	return func(o *Options) {
21 | 		o.Name = n
22 | 	}
23 | }
24 | 
25 | // Size sets the size of the ring buffer.
26 | func Size(s int) Option {
27 | 	return func(o *Options) {
28 | 		o.Size = s
29 | 	}
30 | }
31 | 
32 | func Format(f FormatFunc) Option {
33 | 	return func(o *Options) {
34 | 		o.Format = f
35 | 	}
36 | }
37 | 
38 | // DefaultOptions returns default options.
39 | func DefaultOptions() Options {
40 | 	return Options{
41 | 		Size: DefaultSize,
42 | 	}
43 | }
44 | 
45 | // ReadOptions for querying the logs.
46 | type ReadOptions struct {
47 | 	// Since what time in past to return the logs
48 | 	Since time.Time
49 | 	// Count specifies number of logs to return
50 | 	Count int
51 | 	// Stream requests continuous log stream
52 | 	Stream bool
53 | }
54 | 
55 | // ReadOption used for reading the logs.
56 | type ReadOption func(*ReadOptions)
57 | 
58 | // Since sets the time since which to return the log records.
59 | func Since(s time.Time) ReadOption {
60 | 	return func(o *ReadOptions) {
61 | 		o.Since = s
62 | 	}
63 | }
64 | 
65 | // Count sets the number of log records to return.
66 | func Count(c int) ReadOption {
67 | 	return func(o *ReadOptions) {
68 | 		o.Count = c
69 | 	}
70 | }
71 | 


--------------------------------------------------------------------------------
/debug/log/os.go:
--------------------------------------------------------------------------------
 1 | package log
 2 | 
 3 | import (
 4 | 	"sync"
 5 | 
 6 | 	"github.com/google/uuid"
 7 | 	"go-micro.dev/v5/util/ring"
 8 | )
 9 | 
10 | // Should stream from OS.
11 | type osLog struct {
12 | 	format FormatFunc
13 | 	buffer *ring.Buffer
14 | 	subs   map[string]*osStream
15 | 
16 | 	sync.RWMutex
17 | 	once sync.Once
18 | }
19 | 
20 | type osStream struct {
21 | 	stream chan Record
22 | }
23 | 
24 | // Read reads log entries from the logger.
25 | func (o *osLog) Read(...ReadOption) ([]Record, error) {
26 | 	var records []Record
27 | 
28 | 	// read the last 100 records
29 | 	for _, v := range o.buffer.Get(100) {
30 | 		records = append(records, v.Value.(Record))
31 | 	}
32 | 
33 | 	return records, nil
34 | }
35 | 
36 | // Write writes records to log.
37 | func (o *osLog) Write(r Record) error {
38 | 	o.buffer.Put(r)
39 | 	return nil
40 | }
41 | 
42 | // Stream log records.
43 | func (o *osLog) Stream() (Stream, error) {
44 | 	o.Lock()
45 | 	defer o.Unlock()
46 | 
47 | 	// create stream
48 | 	st := &osStream{
49 | 		stream: make(chan Record, 128),
50 | 	}
51 | 
52 | 	// save stream
53 | 	o.subs[uuid.New().String()] = st
54 | 
55 | 	return st, nil
56 | }
57 | 
58 | func (o *osStream) Chan() <-chan Record {
59 | 	return o.stream
60 | }
61 | 
62 | func (o *osStream) Stop() error {
63 | 	return nil
64 | }
65 | 
66 | func NewLog(opts ...Option) Log {
67 | 	options := Options{
68 | 		Format: DefaultFormat,
69 | 	}
70 | 	for _, o := range opts {
71 | 		o(&options)
72 | 	}
73 | 
74 | 	l := &osLog{
75 | 		format: options.Format,
76 | 		buffer: ring.New(1024),
77 | 		subs:   make(map[string]*osStream),
78 | 	}
79 | 
80 | 	return l
81 | }
82 | 


--------------------------------------------------------------------------------
/debug/profile/http/http.go:
--------------------------------------------------------------------------------
 1 | // Package http enables the http profiler
 2 | package http
 3 | 
 4 | import (
 5 | 	"context"
 6 | 	"net/http"
 7 | 	"net/http/pprof"
 8 | 	"sync"
 9 | 
10 | 	"go-micro.dev/v5/debug/profile"
11 | )
12 | 
13 | type httpProfile struct {
14 | 	server *http.Server
15 | 	sync.Mutex
16 | 	running bool
17 | }
18 | 
19 | var (
20 | 	DefaultAddress = ":6060"
21 | )
22 | 
23 | // Start the profiler.
24 | func (h *httpProfile) Start() error {
25 | 	h.Lock()
26 | 	defer h.Unlock()
27 | 
28 | 	if h.running {
29 | 		return nil
30 | 	}
31 | 
32 | 	go func() {
33 | 		if err := h.server.ListenAndServe(); err != nil {
34 | 			h.Lock()
35 | 			h.running = false
36 | 			h.Unlock()
37 | 		}
38 | 	}()
39 | 
40 | 	h.running = true
41 | 
42 | 	return nil
43 | }
44 | 
45 | // Stop the profiler.
46 | func (h *httpProfile) Stop() error {
47 | 	h.Lock()
48 | 	defer h.Unlock()
49 | 
50 | 	if !h.running {
51 | 		return nil
52 | 	}
53 | 
54 | 	h.running = false
55 | 
56 | 	return h.server.Shutdown(context.TODO())
57 | }
58 | 
59 | func (h *httpProfile) String() string {
60 | 	return "http"
61 | }
62 | 
63 | func NewProfile(opts ...profile.Option) profile.Profile {
64 | 	mux := http.NewServeMux()
65 | 
66 | 	mux.HandleFunc("/debug/pprof/", pprof.Index)
67 | 	mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
68 | 	mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
69 | 	mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
70 | 	mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
71 | 
72 | 	return &httpProfile{
73 | 		server: &http.Server{
74 | 			Addr:    DefaultAddress,
75 | 			Handler: mux,
76 | 		},
77 | 	}
78 | }
79 | 


--------------------------------------------------------------------------------
/debug/profile/profile.go:
--------------------------------------------------------------------------------
 1 | // Package profile is for profilers
 2 | package profile
 3 | 
 4 | type Profile interface {
 5 | 	// Start the profiler
 6 | 	Start() error
 7 | 	// Stop the profiler
 8 | 	Stop() error
 9 | 	// Name of the profiler
10 | 	String() string
11 | }
12 | 
13 | var (
14 | 	DefaultProfile Profile = new(noop)
15 | )
16 | 
17 | type noop struct{}
18 | 
19 | func (p *noop) Start() error {
20 | 	return nil
21 | }
22 | 
23 | func (p *noop) Stop() error {
24 | 	return nil
25 | }
26 | 
27 | func (p *noop) String() string {
28 | 	return "noop"
29 | }
30 | 
31 | type Options struct {
32 | 	// Name to use for the profile
33 | 	Name string
34 | }
35 | 
36 | type Option func(o *Options)
37 | 
38 | // Name of the profile.
39 | func Name(n string) Option {
40 | 	return func(o *Options) {
41 | 		o.Name = n
42 | 	}
43 | }
44 | 


--------------------------------------------------------------------------------
/debug/stats/stats.go:
--------------------------------------------------------------------------------
 1 | // Package stats provides runtime stats
 2 | package stats
 3 | 
 4 | // Stats provides stats interface.
 5 | type Stats interface {
 6 | 	// Read stat snapshot
 7 | 	Read() ([]*Stat, error)
 8 | 	// Write a stat snapshot
 9 | 	Write(*Stat) error
10 | 	// Record a request
11 | 	Record(error) error
12 | }
13 | 
14 | // A runtime stat.
15 | type Stat struct {
16 | 	// Timestamp of recording
17 | 	Timestamp int64
18 | 	// Start time as unix timestamp
19 | 	Started int64
20 | 	// Uptime in seconds
21 | 	Uptime int64
22 | 	// Memory usage in bytes
23 | 	Memory uint64
24 | 	// Threads aka go routines
25 | 	Threads uint64
26 | 	// Garbage collection in nanoseconds
27 | 	GC uint64
28 | 	// Total requests
29 | 	Requests uint64
30 | 	// Total errors
31 | 	Errors uint64
32 | }
33 | 
34 | var (
35 | 	DefaultStats = NewStats()
36 | )
37 | 


--------------------------------------------------------------------------------
/debug/trace/noop.go:
--------------------------------------------------------------------------------
 1 | package trace
 2 | 
 3 | import "context"
 4 | 
 5 | type noop struct{}
 6 | 
 7 | func (n *noop) Init(...Option) error {
 8 | 	return nil
 9 | }
10 | 
11 | func (n *noop) Start(ctx context.Context, name string) (context.Context, *Span) {
12 | 	return nil, nil
13 | }
14 | 
15 | func (n *noop) Finish(*Span) error {
16 | 	return nil
17 | }
18 | 
19 | func (n *noop) Read(...ReadOption) ([]*Span, error) {
20 | 	return nil, nil
21 | }
22 | 


--------------------------------------------------------------------------------
/debug/trace/options.go:
--------------------------------------------------------------------------------
 1 | package trace
 2 | 
 3 | type Options struct {
 4 | 	// Size is the size of ring buffer
 5 | 	Size int
 6 | }
 7 | 
 8 | type Option func(o *Options)
 9 | 
10 | type ReadOptions struct {
11 | 	// Trace id
12 | 	Trace string
13 | }
14 | 
15 | type ReadOption func(o *ReadOptions)
16 | 
17 | // Read the given trace.
18 | func ReadTrace(t string) ReadOption {
19 | 	return func(o *ReadOptions) {
20 | 		o.Trace = t
21 | 	}
22 | }
23 | 
24 | const (
25 | 	// DefaultSize of the buffer.
26 | 	DefaultSize = 64
27 | )
28 | 
29 | // DefaultOptions returns default options.
30 | func DefaultOptions() Options {
31 | 	return Options{
32 | 		Size: DefaultSize,
33 | 	}
34 | }
35 | 


--------------------------------------------------------------------------------
/errors/errors.pb.micro.go:
--------------------------------------------------------------------------------
 1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
 2 | // source: errors.proto
 3 | 
 4 | package errors
 5 | 
 6 | import (
 7 | 	fmt "fmt"
 8 | 	proto "google.golang.org/protobuf/proto"
 9 | 	math "math"
10 | )
11 | 
12 | // Reference imports to suppress errors if they are not otherwise used.
13 | var _ = proto.Marshal
14 | var _ = fmt.Errorf
15 | var _ = math.Inf
16 | 


--------------------------------------------------------------------------------
/errors/errors.proto:
--------------------------------------------------------------------------------
 1 | syntax = "proto3";
 2 | 
 3 | package errors;
 4 | 
 5 | message Error {
 6 |   string id = 1;
 7 |   int32 code = 2;
 8 |   string detail = 3;
 9 |   string status = 4;
10 | };
11 | 
12 | message MultiError {
13 |   repeated Error errors = 1;
14 | }


--------------------------------------------------------------------------------
/event.go:
--------------------------------------------------------------------------------
 1 | package micro
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/client"
 7 | )
 8 | 
 9 | type event struct {
10 | 	c     client.Client
11 | 	topic string
12 | }
13 | 
14 | func (e *event) Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error {
15 | 	return e.c.Publish(ctx, e.c.NewMessage(e.topic, msg), opts...)
16 | }
17 | 


--------------------------------------------------------------------------------
/events/natsjs/README.md:
--------------------------------------------------------------------------------
 1 | # NATS JetStream
 2 | 
 3 | This plugin uses NATS with JetStream to send and receive events.
 4 | 
 5 | ## Create a stream
 6 | 
 7 | ```go
 8 | ev, err := natsjs.NewStream(
 9 |   natsjs.Address("nats://10.0.1.46:4222"),
10 |   natsjs.MaxAge(24*160*time.Minute),
11 | )
12 | ```
13 | 
14 | ## Consume a stream
15 | 
16 | ```go
17 | ee, err := events.Consume("test",
18 |   events.WithAutoAck(false, time.Second*30),
19 |   events.WithGroup("testgroup"),
20 | )
21 | if err != nil {
22 |   panic(err)
23 | }
24 | go func() {
25 |   for {
26 |     msg := <-ee
27 |     // Process the message
28 |     logger.Info("Received message:", string(msg.Payload))
29 |     err := msg.Ack()
30 |     if err != nil {
31 |       logger.Error("Error acknowledging message:", err)
32 |     } else {
33 |       logger.Info("Message acknowledged")
34 |     }
35 |   }
36 | }()
37 | 
38 | ```
39 | 
40 | ## Publish an Event to the stream
41 | 
42 | ```go
43 | err = ev.Publish("test", []byte("hello world"))
44 | if err != nil {
45 |   panic(err)
46 | }
47 | ```
48 | 
49 | 


--------------------------------------------------------------------------------
/events/store_test.go:
--------------------------------------------------------------------------------
 1 | package events
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"github.com/google/uuid"
 7 | 	"github.com/stretchr/testify/assert"
 8 | )
 9 | 
10 | func TestStore(t *testing.T) {
11 | 	store := NewStore()
12 | 
13 | 	testData := []Event{
14 | 		{ID: uuid.New().String(), Topic: "foo"},
15 | 		{ID: uuid.New().String(), Topic: "foo"},
16 | 		{ID: uuid.New().String(), Topic: "bar"},
17 | 	}
18 | 
19 | 	// write the records to the store
20 | 	t.Run("Write", func(t *testing.T) {
21 | 		for _, event := range testData {
22 | 			err := store.Write(&event)
23 | 			assert.Nilf(t, err, "Writing an event should not return an error")
24 | 		}
25 | 	})
26 | 
27 | 	// should not be able to read events from a blank topic
28 | 	t.Run("ReadMissingTopic", func(t *testing.T) {
29 | 		evs, err := store.Read("")
30 | 		assert.Equal(t, err, ErrMissingTopic, "Reading a blank topic should return an error")
31 | 		assert.Nil(t, evs, "No events should be returned")
32 | 	})
33 | 
34 | 	// should only get the events from the topic requested
35 | 	t.Run("ReadTopic", func(t *testing.T) {
36 | 		evs, err := store.Read("foo")
37 | 		assert.Nilf(t, err, "No error should be returned")
38 | 		assert.Len(t, evs, 2, "Only the events for this topic should be returned")
39 | 	})
40 | 
41 | 	// limits should be honoured
42 | 	t.Run("ReadTopicLimit", func(t *testing.T) {
43 | 		evs, err := store.Read("foo", ReadLimit(1))
44 | 		assert.Nilf(t, err, "No error should be returned")
45 | 		assert.Len(t, evs, 1, "The result should include no more than the read limit")
46 | 	})
47 | }
48 | 


--------------------------------------------------------------------------------
/genai/default.go:
--------------------------------------------------------------------------------
 1 | package genai
 2 | 
 3 | import (
 4 | 	"sync"
 5 | )
 6 | 
 7 | var (
 8 | 	DefaultGenAI GenAI = &noopGenAI{}
 9 | 	defaultOnce  sync.Once
10 | )
11 | 
12 | func SetDefault(g GenAI) {
13 | 	defaultOnce.Do(func() {
14 | 		DefaultGenAI = g
15 | 	})
16 | }
17 | 


--------------------------------------------------------------------------------
/genai/genai.go:
--------------------------------------------------------------------------------
 1 | // Package genai provides a generic interface for generative AI providers.
 2 | package genai
 3 | 
 4 | // Result is the unified response from GenAI providers.
 5 | type Result struct {
 6 | 	Prompt string
 7 | 	Type   string
 8 | 	Data   []byte // for audio/image binary data
 9 | 	Text   string // for text or image URL
10 | }
11 | 
12 | // Stream represents a streaming response from a GenAI provider.
13 | type Stream struct {
14 | 	Results <-chan *Result
15 | 	Err     error
16 | 	// You can add fields for cancellation, errors, etc. if needed
17 | }
18 | 
19 | // GenAI is the generic interface for generative AI providers.
20 | type GenAI interface {
21 | 	Generate(prompt string, opts ...Option) (*Result, error)
22 | 	Stream(prompt string, opts ...Option) (*Stream, error)
23 | 	String() string
24 | }
25 | 
26 | // Option is a functional option for configuring providers.
27 | type Option func(*Options)
28 | 
29 | // Options holds configuration for providers.
30 | type Options struct {
31 | 	APIKey   string
32 | 	Endpoint string
33 | 	Type     string // "text", "image", "audio", etc.
34 | 	Model    string // model name, e.g. "gemini-2.5-pro"
35 | 	// Add more fields as needed
36 | }
37 | 
38 | // Option functions for generation type
39 | func Text(o *Options)  { o.Type = "text" }
40 | func Image(o *Options) { o.Type = "image" }
41 | func Audio(o *Options) { o.Type = "audio" }
42 | 
43 | // Provider registry
44 | var providers = make(map[string]GenAI)
45 | 
46 | // Register a GenAI provider by name.
47 | func Register(name string, provider GenAI) {
48 | 	providers[name] = provider
49 | }
50 | 
51 | // Get a GenAI provider by name.
52 | func Get(name string) GenAI {
53 | 	return providers[name]
54 | }
55 | 


--------------------------------------------------------------------------------
/genai/noop.go:
--------------------------------------------------------------------------------
 1 | package genai
 2 | 
 3 | type noopGenAI struct{}
 4 | 
 5 | func (n *noopGenAI) Generate(prompt string, opts ...Option) (*Result, error) {
 6 | 	return &Result{Prompt: prompt, Type: "noop", Text: "noop response"}, nil
 7 | }
 8 | 
 9 | func (n *noopGenAI) Stream(prompt string, opts ...Option) (*Stream, error) {
10 | 	results := make(chan *Result, 1)
11 | 	results <- &Result{Prompt: prompt, Type: "noop", Text: "noop response"}
12 | 	close(results)
13 | 	return &Stream{Results: results}, nil
14 | }
15 | 
16 | func (n *noopGenAI) String() string {
17 | 	return "noop"
18 | }
19 | 
20 | var Default = &noopGenAI{}
21 | 


--------------------------------------------------------------------------------
/genai/openai/openai_test.go:
--------------------------------------------------------------------------------
 1 | package openai
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/genai"
 5 | 	"os"
 6 | 	"testing"
 7 | )
 8 | 
 9 | func TestOpenAI_GenerateText(t *testing.T) {
10 | 	apiKey := os.Getenv("OPENAI_API_KEY")
11 | 	if apiKey == "" {
12 | 		t.Skip("OPENAI_API_KEY not set")
13 | 	}
14 | 	client := New(genai.WithAPIKey(apiKey))
15 | 	res, err := client.Generate("Say hello world", genai.Text)
16 | 	if err != nil {
17 | 		t.Fatalf("Generate error: %v", err)
18 | 	}
19 | 	if res == nil || res.Text == "" {
20 | 		t.Error("Expected non-empty text response")
21 | 	}
22 | }
23 | 
24 | func TestOpenAI_GenerateImage(t *testing.T) {
25 | 	apiKey := os.Getenv("OPENAI_API_KEY")
26 | 	if apiKey == "" {
27 | 		t.Skip("OPENAI_API_KEY not set")
28 | 	}
29 | 	client := New(genai.WithAPIKey(apiKey))
30 | 	res, err := client.Generate("A cat wearing sunglasses", genai.Image)
31 | 	if err != nil {
32 | 		t.Fatalf("Generate error: %v", err)
33 | 	}
34 | 	if res == nil || res.Text == "" {
35 | 		t.Error("Expected non-empty image URL")
36 | 	}
37 | }
38 | 


--------------------------------------------------------------------------------
/genai/options.go:
--------------------------------------------------------------------------------
 1 | package genai
 2 | 
 3 | // Option sets options for a GenAI provider.
 4 | func WithAPIKey(key string) Option {
 5 | 	return func(o *Options) {
 6 | 		o.APIKey = key
 7 | 	}
 8 | }
 9 | 
10 | func WithEndpoint(endpoint string) Option {
11 | 	return func(o *Options) {
12 | 		o.Endpoint = endpoint
13 | 	}
14 | }
15 | 
16 | func WithModel(model string) Option {
17 | 	return func(o *Options) {
18 | 		o.Model = model
19 | 	}
20 | }
21 | 


--------------------------------------------------------------------------------
/internal/README.md:
--------------------------------------------------------------------------------
1 | Internal related things
2 | 


--------------------------------------------------------------------------------
/internal/website/.gitignore:
--------------------------------------------------------------------------------
1 | _site
2 | 


--------------------------------------------------------------------------------
/internal/website/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | 
3 | source "https://rubygems.org"
4 | 
5 | # gem "rails"
6 | gem 'github-pages', group: :jekyll_plugins
7 | 


--------------------------------------------------------------------------------
/internal/website/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 | 
3 | The Go Micro website including docs
4 | 


--------------------------------------------------------------------------------
/internal/website/_config.yml:
--------------------------------------------------------------------------------
1 | title: Docs
2 | description: "A Go microservices framework"
3 | baseurl: "" # the subpath of your site, e.g. /blog
4 | url: "" # the base hostname & protocol for your site, e.g. http://example.com
5 | 
6 | theme: jekyll-theme-primer
7 | 


--------------------------------------------------------------------------------
/internal/website/_layouts/default.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html lang="en">
 3 | <head>
 4 |   <meta charset="UTF-8">
 5 |   <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6 |   <title>{% if page.title %}{{ page.title }} | {% endif %}Go Micro Documentation</title>
 7 |   <style>
 8 |     body { font-family: sans-serif; margin: 0; padding: 0; background: #f9f9f9; }
 9 |     header { display: flex; align-items: center; justify-content: space-between; padding: 1rem 2rem; background: #fff; border-bottom: 1px solid #eee; }
10 |     .logo-link { display: flex; align-items: center; text-decoration: none; }
11 |     .logo-link img { height: 40px; margin-right: 10px; }
12 |     nav a { margin-left: 24px; color: #333; text-decoration: none; font-weight: 500; }
13 |     nav a:hover { color: #007d9c; }
14 |     main { max-width: 800px; margin: 2rem auto; background: #fff; padding: 2rem; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.03); }
15 |     pre { background: whitesmoke; padding: 10px; border-radius: 5px; }
16 |   </style>
17 | </head>
18 | <body>
19 |   <header>
20 |     <a class="logo-link" href="/">
21 |       <img src="/images/logo.png" alt="Go Micro Logo">
22 |       <span style="font-size: 1.3rem; font-weight: bold; color: #222;">Go Micro</span>
23 |     </a>
24 |     <nav>
25 |       <a href="/docs/">Docs</a>
26 |       <a href="https://github.com/micro/go-micro" target="_blank" rel="noopener">GitHub</a>
27 |       <a href="/">Home</a>
28 |     </nav>
29 |   </header>
30 |   <main>
31 |     {{ content }}
32 |   </main>
33 | </body>
34 | </html>
35 | 


--------------------------------------------------------------------------------
/internal/website/docs/broker.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | layout: default
 3 | ---
 4 | 
 5 | # Broker
 6 | 
 7 | The broker provides pub/sub messaging for Go Micro services.
 8 | 
 9 | ## Features
10 | - Publish messages to topics
11 | - Subscribe to topics
12 | - Multiple broker implementations
13 | 
14 | ## Implementations
15 | Supported brokers include:
16 | - Memory (default)
17 | - NATS
18 | - RabbitMQ
19 | 
20 | Configure the broker when creating your service as needed.
21 | 
22 | ## Example Usage
23 | 
24 | Here's how to use the broker in your Go Micro service:
25 | 
26 | ```go
27 | package main
28 | 
29 | import (
30 |     "go-micro.dev/v5"
31 |     "go-micro.dev/v5/broker"
32 |     "log"
33 | )
34 | 
35 | func main() {
36 |     service := micro.NewService()
37 |     service.Init()
38 | 
39 |     // Publish a message
40 |     if err := broker.Publish("topic", &broker.Message{Body: []byte("hello world")}); err != nil {
41 |         log.Fatal(err)
42 |     }
43 | 
44 |     // Subscribe to a topic
45 |     _, err := broker.Subscribe("topic", func(p broker.Event) error {
46 |         log.Printf("Received message: %s", string(p.Message().Body))
47 |         return nil
48 |     })
49 |     if err != nil {
50 |         log.Fatal(err)
51 |     }
52 | 
53 |     // Run the service
54 |     if err := service.Run(); err != nil {
55 |         log.Fatal(err)
56 |     }
57 | }
58 | ```
59 | 


--------------------------------------------------------------------------------
/internal/website/docs/client-server.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | layout: default
 3 | ---
 4 | 
 5 | # Client/Server
 6 | 
 7 | Go Micro uses a client/server model for RPC communication between services.
 8 | 
 9 | ## Client
10 | The client is used to make requests to other services.
11 | 
12 | ## Server
13 | The server handles incoming requests.
14 | 
15 | Both client and server are pluggable and support middleware wrappers for additional functionality.
16 | 
17 | ## Example Usage
18 | 
19 | Here's how to define a simple handler and register it with a Go Micro server:
20 | 
21 | ```go
22 | package main
23 | 
24 | import (
25 |     "context"
26 |     "go-micro.dev/v5"
27 |     "log"
28 | )
29 | 
30 | type Greeter struct{}
31 | 
32 | func (g *Greeter) Hello(ctx context.Context, req *struct{}, rsp *struct{Msg string}) error {
33 |     rsp.Msg = "Hello, world!"
34 |     return nil
35 | }
36 | 
37 | func main() {
38 |     service := micro.NewService(
39 |         micro.Name("greeter"),
40 |     )
41 |     service.Init()
42 |     micro.RegisterHandler(service.Server(), new(Greeter))
43 |     if err := service.Run(); err != nil {
44 |         log.Fatal(err)
45 |     }
46 | }
47 | ```
48 | 


--------------------------------------------------------------------------------
/internal/website/docs/index.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | layout: default
 3 | ---
 4 | 
 5 | # Docs
 6 | 
 7 | Documentation for the Go Micro framework 
 8 | 
 9 | ## Overview
10 | 
11 | Go Micro is a framework for microservices development. 
12 | It's built on a powerful pluggable architecture using 
13 | Go interfaces. Go Micro defines the foundations for 
14 | distributed systems development which includes 
15 | service discovery, client/server rpc and pubsub. 
16 | Additionally Go Micro contains other primitives 
17 | such as auth, caching and storage. All of this 
18 | is encapsulated in a high level service interface.
19 | 
20 | ## Learn More
21 | 
22 | To get started follow the getting started guide. 
23 | Otherwise continue to read the docs for more information 
24 | about the framework.
25 | 
26 | ## Contents
27 | 
28 | - [Getting Started](getting-started.md)
29 | - [Architecture](architecture.md)
30 | - [Registry](registry.md)
31 | - [Broker](broker.md)
32 | - [Client/Server](client-server.md)
33 | - [Transport](transport.md)
34 | - [Store](store.md)
35 | 


--------------------------------------------------------------------------------
/internal/website/docs/registry.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | layout: default
 3 | ---
 4 | 
 5 | # Registry
 6 | 
 7 | The registry is responsible for service discovery in Go Micro. It allows services to register themselves and discover other services.
 8 | 
 9 | ## Features
10 | - Service registration and deregistration
11 | - Service lookup
12 | - Watch for changes
13 | 
14 | ## Implementations
15 | Go Micro supports multiple registry backends, including:
16 | - MDNS (default)
17 | - Consul
18 | - Etcd
19 | - NATS
20 | 
21 | You can configure the registry when initializing your service.
22 | 
23 | ## Example Usage
24 | 
25 | Here's how to use a custom registry (e.g., Consul) in your Go Micro service:
26 | 
27 | ```go
28 | package main
29 | 
30 | import (
31 |     "go-micro.dev/v5"
32 |     "go-micro.dev/v5/registry/consul"
33 | )
34 | 
35 | func main() {
36 |     reg := consul.NewRegistry()
37 |     service := micro.NewService(
38 |         micro.Registry(reg),
39 |     )
40 |     service.Init()
41 |     service.Run()
42 | }
43 | ```
44 | 


--------------------------------------------------------------------------------
/internal/website/docs/store.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | layout: default
 3 | ---
 4 | 
 5 | # Store
 6 | 
 7 | The store provides a pluggable interface for data storage in Go Micro.
 8 | 
 9 | ## Features
10 | - Key-value storage
11 | - Multiple backend support
12 | 
13 | ## Implementations
14 | Supported stores include:
15 | - Memory (default)
16 | - File
17 | - MySQL
18 | - Redis
19 | 
20 | Configure the store as needed for your application.
21 | 
22 | ## Example Usage
23 | 
24 | Here's how to use the store in your Go Micro service:
25 | 
26 | ```go
27 | package main
28 | 
29 | import (
30 |     "go-micro.dev/v5"
31 |     "go-micro.dev/v5/store"
32 |     "log"
33 | )
34 | 
35 | func main() {
36 |     service := micro.NewService()
37 |     service.Init()
38 | 
39 |     // Write a record
40 |     if err := store.Write(&store.Record{Key: "foo", Value: []byte("bar")}); err != nil {
41 |         log.Fatal(err)
42 |     }
43 | 
44 |     // Read a record
45 |     recs, err := store.Read("foo")
46 |     if err != nil {
47 |         log.Fatal(err)
48 |     }
49 |     log.Printf("Read value: %s", string(recs[0].Value))
50 | }
51 | ```
52 | 


--------------------------------------------------------------------------------
/internal/website/docs/transport.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | layout: default
 3 | ---
 4 | 
 5 | # Transport
 6 | 
 7 | The transport layer is responsible for communication between services.
 8 | 
 9 | ## Features
10 | - Pluggable transport implementations
11 | - Secure and efficient communication
12 | 
13 | ## Implementations
14 | Supported transports include:
15 | - TCP (default)
16 | - gRPC
17 | 
18 | You can specify the transport when initializing your service.
19 | 
20 | ## Example Usage
21 | 
22 | Here's how to use a custom transport (e.g., gRPC) in your Go Micro service:
23 | 
24 | ```go
25 | package main
26 | 
27 | import (
28 |     "go-micro.dev/v5"
29 |     "go-micro.dev/v5/transport/grpc"
30 | )
31 | 
32 | func main() {
33 |     t := grpc.NewTransport()
34 |     service := micro.NewService(
35 |         micro.Transport(t),
36 |     )
37 |     service.Init()
38 |     service.Run()
39 | }
40 | ```
41 | 


--------------------------------------------------------------------------------
/internal/website/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/micro/go-micro/01ed999b2ac8505c2bf470eef067a7f77fef30b9/internal/website/images/logo.png


--------------------------------------------------------------------------------
/internal/website/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | <!DOCTYPE html>
 3 | <html>
 4 |   <head>
 5 |     <meta charset="UTF-8" />
 6 |     <meta name="viewport" content="width=device-width" />
 7 |     <meta name="go-import" content="go-micro.dev/v5 git https://github.com/micro/go-micro">
 8 |     <meta name="go-source" content="go-micro.dev/5 https://github.com/micro/go-micro https://github.com/micro/go-micro/tree/master{/dir} https://github.com/micro/go-micro/blob/master{/dir}/{file}#L{line}">
 9 |     <title>Go Micro</title>
10 |     <style>
11 |       body {
12 |         font-family: Arial;
13 |         font-size: 16px;
14 |         margin: 25px;
15 |       }
16 |       .container {
17 |          max-width: 250px;
18 |          margin: 0 auto;
19 |          padding-top: 100px;
20 |          text-align: center;
21 |       }
22 |       a { color: black; text-decoration: none; font-weight: bold; padding: 10px;}
23 |       pre { background: #f5f5f5; border-radius: 5px; padding: 10px;}
24 |     </style>
25 |   </head>
26 | 
27 |   <body>
28 |      <div class="container">
29 |         <!-- <h1>Go Micro</h1> -->
30 |         <a href="https://github.com/micro/go-micro">
31 |           <img src="https://raw.githubusercontent.com/micro/go-micro/master/logo.png" style="height: auto; width: 100%;" />
32 |         </a>
33 |         <p>A Go microservices framework</p>
34 |         <pre>go get go-micro.dev/v5</pre>
35 |      </div>
36 |   </body>
37 | </html>
38 | 
39 | 


--------------------------------------------------------------------------------
/logger/context.go:
--------------------------------------------------------------------------------
 1 | package logger
 2 | 
 3 | import "context"
 4 | 
 5 | type loggerKey struct{}
 6 | 
 7 | func FromContext(ctx context.Context) (Logger, bool) {
 8 | 	l, ok := ctx.Value(loggerKey{}).(Logger)
 9 | 	return l, ok
10 | }
11 | 
12 | func NewContext(ctx context.Context, l Logger) context.Context {
13 | 	return context.WithValue(ctx, loggerKey{}, l)
14 | }
15 | 


--------------------------------------------------------------------------------
/logger/logger.go:
--------------------------------------------------------------------------------
 1 | // Package log provides a log interface
 2 | package logger
 3 | 
 4 | var (
 5 | 	// Default logger.
 6 | 	DefaultLogger Logger = NewLogger()
 7 | 
 8 | 	// Default logger helper.
 9 | 	DefaultHelper *Helper = NewHelper(DefaultLogger)
10 | )
11 | 
12 | // Logger is a generic logging interface.
13 | type Logger interface {
14 | 	// Init initializes options
15 | 	Init(options ...Option) error
16 | 	// The Logger options
17 | 	Options() Options
18 | 	// Fields set fields to always be logged
19 | 	Fields(fields map[string]interface{}) Logger
20 | 	// Log writes a log entry
21 | 	Log(level Level, v ...interface{})
22 | 	// Logf writes a formatted log entry
23 | 	Logf(level Level, format string, v ...interface{})
24 | 	// String returns the name of logger
25 | 	String() string
26 | }
27 | 
28 | func Init(opts ...Option) error {
29 | 	return DefaultLogger.Init(opts...)
30 | }
31 | 
32 | func Fields(fields map[string]interface{}) Logger {
33 | 	return DefaultLogger.Fields(fields)
34 | }
35 | 
36 | func Log(level Level, v ...interface{}) {
37 | 	DefaultLogger.Log(level, v...)
38 | }
39 | 
40 | func Logf(level Level, format string, v ...interface{}) {
41 | 	DefaultLogger.Logf(level, format, v...)
42 | }
43 | 
44 | func String() string {
45 | 	return DefaultLogger.String()
46 | }
47 | 
48 | func LoggerOrDefault(l Logger) Logger {
49 | 	if l == nil {
50 | 		return DefaultLogger
51 | 	}
52 | 	return l
53 | }
54 | 


--------------------------------------------------------------------------------
/logger/logger_test.go:
--------------------------------------------------------------------------------
 1 | package logger
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"testing"
 6 | )
 7 | 
 8 | func TestLogger(t *testing.T) {
 9 | 	l := NewLogger(WithLevel(TraceLevel), WithCallerSkipCount(2))
10 | 
11 | 	h1 := NewHelper(l).WithFields(map[string]interface{}{"key1": "val1"})
12 | 	h1.Log(TraceLevel, "simple log before trace_msg1")
13 | 	h1.Trace("trace_msg1")
14 | 	h1.Log(TraceLevel, "simple log after trace_msg1")
15 | 	h1.Warn("warn_msg1")
16 | 
17 | 	h2 := NewHelper(l).WithFields(map[string]interface{}{"key2": "val2"})
18 | 	h2.Logf(TraceLevel, "formatted log before trace_msg%s", "2")
19 | 	h2.Trace("trace_msg2")
20 | 	h2.Logf(TraceLevel, "formatted log after trace_msg%s", "2")
21 | 	h2.Warn("warn_msg2")
22 | 
23 | 	l = NewLogger(WithLevel(TraceLevel), WithCallerSkipCount(1))
24 | 	l.Fields(map[string]interface{}{"key3": "val4"}).Log(InfoLevel, "test_msg")
25 | }
26 | 
27 | func TestExtract(t *testing.T) {
28 | 	l := NewLogger(WithLevel(TraceLevel), WithCallerSkipCount(2)).Fields(map[string]interface{}{"requestID": "req-1"})
29 | 
30 | 	ctx := NewContext(context.Background(), l)
31 | 
32 | 	Info("info message without request ID")
33 | 	Extract(ctx).Info("info message with request ID")
34 | }
35 | 


--------------------------------------------------------------------------------
/logger/options.go:
--------------------------------------------------------------------------------
 1 | package logger
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"io"
 6 | )
 7 | 
 8 | type Option func(*Options)
 9 | 
10 | type Options struct {
11 | 	// It's common to set this to a file, or leave it default which is `os.Stderr`
12 | 	Out io.Writer
13 | 	// Alternative options
14 | 	Context context.Context
15 | 	// fields to always be logged
16 | 	Fields map[string]interface{}
17 | 	// Caller skip frame count for file:line info
18 | 	CallerSkipCount int
19 | 	// The logging level the logger should log at. default is `InfoLevel`
20 | 	Level Level
21 | }
22 | 
23 | // WithFields set default fields for the logger.
24 | func WithFields(fields map[string]interface{}) Option {
25 | 	return func(args *Options) {
26 | 		args.Fields = fields
27 | 	}
28 | }
29 | 
30 | // WithLevel set default level for the logger.
31 | func WithLevel(level Level) Option {
32 | 	return func(args *Options) {
33 | 		args.Level = level
34 | 	}
35 | }
36 | 
37 | // WithOutput set default output writer for the logger.
38 | func WithOutput(out io.Writer) Option {
39 | 	return func(args *Options) {
40 | 		args.Out = out
41 | 	}
42 | }
43 | 
44 | // WithCallerSkipCount set frame count to skip.
45 | func WithCallerSkipCount(c int) Option {
46 | 	return func(args *Options) {
47 | 		args.CallerSkipCount = c
48 | 	}
49 | }
50 | 
51 | func SetOption(k, v interface{}) Option {
52 | 	return func(o *Options) {
53 | 		if o.Context == nil {
54 | 			o.Context = context.Background()
55 | 		}
56 | 		o.Context = context.WithValue(o.Context, k, v)
57 | 	}
58 | }
59 | 


--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/micro/go-micro/01ed999b2ac8505c2bf470eef067a7f77fef30b9/logo.png


--------------------------------------------------------------------------------
/options.go:
--------------------------------------------------------------------------------
 1 | package micro
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/service"
 5 | )
 6 | 
 7 | var Broker = service.Broker
 8 | var Cache = service.Cache
 9 | var Cmd = service.Cmd
10 | var Client = service.Client
11 | var Context = service.Context
12 | var Handle = service.Handle
13 | var HandleSignal = service.HandleSignal
14 | var Profile = service.Profile
15 | var Server = service.Server
16 | var Store = service.Store
17 | var Registry = service.Registry
18 | var Tracer = service.Tracer
19 | var Auth = service.Auth
20 | var Config = service.Config
21 | var Selector = service.Selector
22 | var Transport = service.Transport
23 | var Address = service.Address
24 | var Name = service.Name
25 | var Version = service.Version
26 | var Metadata = service.Metadata
27 | var Flags = service.Flags
28 | var Action = service.Action
29 | var RegisterTTL = service.RegisterTTL
30 | var RegisterInterval = service.RegisterInterval
31 | var WrapClient = service.WrapClient
32 | var WrapCall = service.WrapCall
33 | var WrapHandler = service.WrapHandler
34 | var WrapSubscriber = service.WrapSubscriber
35 | var AddListenOption = service.AddListenOption
36 | var BeforeStart = service.BeforeStart
37 | var BeforeStop = service.BeforeStop
38 | var AfterStart = service.AfterStart
39 | var AfterStop = service.AfterStop
40 | var Logger = service.Logger
41 | 


--------------------------------------------------------------------------------
/registry/cache/README.md:
--------------------------------------------------------------------------------
 1 | # Registry Cache
 2 | 
 3 | Cache is a library that provides a caching layer for the go-micro [registry](https://godoc.org/github.com/micro/go-micro/registry#Registry).
 4 | 
 5 | If you're looking for caching in your microservices use the [selector](https://micro.mu/docs/fault-tolerance.html#caching-discovery).
 6 | 
 7 | ## Interface
 8 | 
 9 | ```go
10 | // Cache is the registry cache interface
11 | type Cache interface {
12 | 	// embed the registry interface
13 | 	registry.Registry
14 | 	// stop the cache watcher
15 | 	Stop()
16 | }
17 | ```
18 | 
19 | ## Usage
20 | 
21 | ```go
22 | import (
23 | 	"github.com/micro/go-micro/registry"
24 | 	"github.com/micro/go-micro/registry/cache"
25 | )
26 | 
27 | r := registry.NewRegistry()
28 | cache := cache.New(r)
29 | 
30 | services, _ := cache.GetService("my.service")
31 | ```
32 | 


--------------------------------------------------------------------------------
/registry/cache/options.go:
--------------------------------------------------------------------------------
 1 | package cache
 2 | 
 3 | import (
 4 | 	"time"
 5 | 
 6 | 	"go-micro.dev/v5/logger"
 7 | )
 8 | 
 9 | // WithTTL sets the cache TTL.
10 | func WithTTL(t time.Duration) Option {
11 | 	return func(o *Options) {
12 | 		o.TTL = t
13 | 	}
14 | }
15 | 
16 | // WithLogger sets the underline logger.
17 | func WithLogger(l logger.Logger) Option {
18 | 	return func(o *Options) {
19 | 		o.Logger = l
20 | 	}
21 | }
22 | 


--------------------------------------------------------------------------------
/registry/etcd/options.go:
--------------------------------------------------------------------------------
 1 | package etcd
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/registry"
 7 | 	"go.uber.org/zap"
 8 | )
 9 | 
10 | type authKey struct{}
11 | 
12 | type logConfigKey struct{}
13 | 
14 | type authCreds struct {
15 | 	Username string
16 | 	Password string
17 | }
18 | 
19 | // Auth allows you to specify username/password.
20 | func Auth(username, password string) registry.Option {
21 | 	return func(o *registry.Options) {
22 | 		if o.Context == nil {
23 | 			o.Context = context.Background()
24 | 		}
25 | 		o.Context = context.WithValue(o.Context, authKey{}, &authCreds{Username: username, Password: password})
26 | 	}
27 | }
28 | 
29 | // LogConfig allows you to set etcd log config.
30 | func LogConfig(config *zap.Config) registry.Option {
31 | 	return func(o *registry.Options) {
32 | 		if o.Context == nil {
33 | 			o.Context = context.Background()
34 | 		}
35 | 		o.Context = context.WithValue(o.Context, logConfigKey{}, config)
36 | 	}
37 | }
38 | 


--------------------------------------------------------------------------------
/registry/memory_watcher.go:
--------------------------------------------------------------------------------
 1 | package registry
 2 | 
 3 | import (
 4 | 	"errors"
 5 | )
 6 | 
 7 | type memWatcher struct {
 8 | 	wo   WatchOptions
 9 | 	res  chan *Result
10 | 	exit chan bool
11 | 	id   string
12 | }
13 | 
14 | func (m *memWatcher) Next() (*Result, error) {
15 | 	for {
16 | 		select {
17 | 		case r := <-m.res:
18 | 			if len(m.wo.Service) > 0 && m.wo.Service != r.Service.Name {
19 | 				continue
20 | 			}
21 | 			return r, nil
22 | 		case <-m.exit:
23 | 			return nil, errors.New("watcher stopped")
24 | 		}
25 | 	}
26 | }
27 | 
28 | func (m *memWatcher) Stop() {
29 | 	select {
30 | 	case <-m.exit:
31 | 		return
32 | 	default:
33 | 		close(m.exit)
34 | 	}
35 | }
36 | 


--------------------------------------------------------------------------------
/registry/nats/nats_assert_test.go:
--------------------------------------------------------------------------------
 1 | package nats_test
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | )
 7 | 
 8 | func assertNoError(tb testing.TB, actual error) {
 9 | 	if actual != nil {
10 | 		tb.Errorf("expected no error, got %v", actual)
11 | 	}
12 | }
13 | 
14 | func assertEqual(tb testing.TB, expected, actual interface{}) {
15 | 	if !reflect.DeepEqual(expected, actual) {
16 | 		tb.Errorf("expected %v, got %v", expected, actual)
17 | 	}
18 | }
19 | 


--------------------------------------------------------------------------------
/registry/nats/nats_environment_test.go:
--------------------------------------------------------------------------------
 1 | package nats_test
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"testing"
 6 | 
 7 | 	log "go-micro.dev/v5/logger"
 8 | 	"go-micro.dev/v5/registry"
 9 | 	"go-micro.dev/v5/registry/nats"
10 | )
11 | 
12 | type environment struct {
13 | 	registryOne   registry.Registry
14 | 	registryTwo   registry.Registry
15 | 	registryThree registry.Registry
16 | 
17 | 	serviceOne registry.Service
18 | 	serviceTwo registry.Service
19 | 
20 | 	nodeOne   registry.Node
21 | 	nodeTwo   registry.Node
22 | 	nodeThree registry.Node
23 | }
24 | 
25 | var e environment
26 | 
27 | func TestMain(m *testing.M) {
28 | 	natsURL := os.Getenv("NATS_URL")
29 | 	if natsURL == "" {
30 | 		log.Infof("NATS_URL is undefined - skipping tests")
31 | 		return
32 | 	}
33 | 
34 | 	e.registryOne = nats.NewNatsRegistry(registry.Addrs(natsURL), nats.Quorum(1))
35 | 	e.registryTwo = nats.NewNatsRegistry(registry.Addrs(natsURL), nats.Quorum(1))
36 | 	e.registryThree = nats.NewNatsRegistry(registry.Addrs(natsURL), nats.Quorum(1))
37 | 
38 | 	e.serviceOne.Name = "one"
39 | 	e.serviceOne.Version = "default"
40 | 	e.serviceOne.Nodes = []*registry.Node{&e.nodeOne}
41 | 
42 | 	e.serviceTwo.Name = "two"
43 | 	e.serviceTwo.Version = "default"
44 | 	e.serviceTwo.Nodes = []*registry.Node{&e.nodeOne, &e.nodeTwo}
45 | 
46 | 	e.nodeOne.Id = "one"
47 | 	e.nodeTwo.Id = "two"
48 | 	e.nodeThree.Id = "three"
49 | 
50 | 	if err := e.registryOne.Register(&e.serviceOne); err != nil {
51 | 		log.Fatal(err)
52 | 	}
53 | 
54 | 	if err := e.registryOne.Register(&e.serviceTwo); err != nil {
55 | 		log.Fatal(err)
56 | 	}
57 | 
58 | 	result := m.Run()
59 | 
60 | 	if err := e.registryOne.Deregister(&e.serviceOne); err != nil {
61 | 		log.Fatal(err)
62 | 	}
63 | 
64 | 	if err := e.registryOne.Deregister(&e.serviceTwo); err != nil {
65 | 		log.Fatal(err)
66 | 	}
67 | 
68 | 	os.Exit(result)
69 | }
70 | 


--------------------------------------------------------------------------------
/registry/nats/nats_registry.go:
--------------------------------------------------------------------------------
1 | package nats
2 | 
3 | var (
4 | 	DefaultRegistry = NewNatsRegistry()
5 | )
6 | 


--------------------------------------------------------------------------------
/registry/nats/nats_watcher.go:
--------------------------------------------------------------------------------
 1 | package nats
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"time"
 6 | 
 7 | 	"github.com/nats-io/nats.go"
 8 | 	"go-micro.dev/v5/registry"
 9 | )
10 | 
11 | type natsWatcher struct {
12 | 	sub *nats.Subscription
13 | 	wo  registry.WatchOptions
14 | }
15 | 
16 | func (n *natsWatcher) Next() (*registry.Result, error) {
17 | 	var result *registry.Result
18 | 	for {
19 | 		m, err := n.sub.NextMsg(time.Minute)
20 | 		if err != nil && err == nats.ErrTimeout {
21 | 			continue
22 | 		} else if err != nil {
23 | 			return nil, err
24 | 		}
25 | 		if err := json.Unmarshal(m.Data, &result); err != nil {
26 | 			return nil, err
27 | 		}
28 | 		if len(n.wo.Service) > 0 && result.Service.Name != n.wo.Service {
29 | 			continue
30 | 		}
31 | 		break
32 | 	}
33 | 
34 | 	return result, nil
35 | }
36 | 
37 | func (n *natsWatcher) Stop() {
38 | 	n.sub.Unsubscribe()
39 | }
40 | 


--------------------------------------------------------------------------------
/registry/watcher.go:
--------------------------------------------------------------------------------
 1 | package registry
 2 | 
 3 | import "time"
 4 | 
 5 | // Watcher is an interface that returns updates
 6 | // about services within the registry.
 7 | type Watcher interface {
 8 | 	// Next is a blocking call
 9 | 	Next() (*Result, error)
10 | 	Stop()
11 | }
12 | 
13 | // Result is returned by a call to Next on
14 | // the watcher. Actions can be create, update, delete.
15 | type Result struct {
16 | 	Service *Service
17 | 	Action  string
18 | }
19 | 
20 | // EventType defines registry event type.
21 | type EventType int
22 | 
23 | const (
24 | 	// Create is emitted when a new service is registered.
25 | 	Create EventType = iota
26 | 	// Delete is emitted when an existing service is deregsitered.
27 | 	Delete
28 | 	// Update is emitted when an existing servicec is updated.
29 | 	Update
30 | )
31 | 
32 | // String returns human readable event type.
33 | func (t EventType) String() string {
34 | 	switch t {
35 | 	case Create:
36 | 		return "create"
37 | 	case Delete:
38 | 		return "delete"
39 | 	case Update:
40 | 		return "update"
41 | 	default:
42 | 		return "unknown"
43 | 	}
44 | }
45 | 
46 | // Event is registry event.
47 | type Event struct {
48 | 	// Timestamp is event timestamp
49 | 	Timestamp time.Time
50 | 	// Service is registry service
51 | 	Service *Service
52 | 	// Id is registry id
53 | 	Id string
54 | 	// Type defines type of event
55 | 	Type EventType
56 | }
57 | 


--------------------------------------------------------------------------------
/selector/common_test.go:
--------------------------------------------------------------------------------
 1 | package selector
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/registry"
 5 | )
 6 | 
 7 | var (
 8 | 	// mock data.
 9 | 	testData = map[string][]*registry.Service{
10 | 		"foo": {
11 | 			{
12 | 				Name:    "foo",
13 | 				Version: "1.0.0",
14 | 				Nodes: []*registry.Node{
15 | 					{
16 | 						Id:      "foo-1.0.0-123",
17 | 						Address: "localhost:9999",
18 | 					},
19 | 					{
20 | 						Id:      "foo-1.0.0-321",
21 | 						Address: "localhost:9999",
22 | 					},
23 | 				},
24 | 			},
25 | 			{
26 | 				Name:    "foo",
27 | 				Version: "1.0.1",
28 | 				Nodes: []*registry.Node{
29 | 					{
30 | 						Id:      "foo-1.0.1-321",
31 | 						Address: "localhost:6666",
32 | 					},
33 | 				},
34 | 			},
35 | 			{
36 | 				Name:    "foo",
37 | 				Version: "1.0.3",
38 | 				Nodes: []*registry.Node{
39 | 					{
40 | 						Id:      "foo-1.0.3-345",
41 | 						Address: "localhost:8888",
42 | 					},
43 | 				},
44 | 			},
45 | 		},
46 | 	}
47 | )
48 | 


--------------------------------------------------------------------------------
/selector/default_test.go:
--------------------------------------------------------------------------------
 1 | package selector
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"testing"
 6 | 
 7 | 	"go-micro.dev/v5/registry"
 8 | )
 9 | 
10 | func TestRegistrySelector(t *testing.T) {
11 | 	counts := map[string]int{}
12 | 
13 | 	r := registry.NewMemoryRegistry(registry.Services(testData))
14 | 	cache := NewSelector(Registry(r))
15 | 
16 | 	next, err := cache.Select("foo")
17 | 	if err != nil {
18 | 		t.Errorf("Unexpected error calling cache select: %v", err)
19 | 	}
20 | 
21 | 	for i := 0; i < 100; i++ {
22 | 		node, err := next()
23 | 		if err != nil {
24 | 			t.Errorf("Expected node err, got err: %v", err)
25 | 		}
26 | 		counts[node.Id]++
27 | 	}
28 | 
29 | 	if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
30 | 		t.Logf("Selector Counts %v", counts)
31 | 	}
32 | }
33 | 


--------------------------------------------------------------------------------
/selector/options.go:
--------------------------------------------------------------------------------
 1 | package selector
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/logger"
 7 | 	"go-micro.dev/v5/registry"
 8 | )
 9 | 
10 | type Options struct {
11 | 	Registry registry.Registry
12 | 	Strategy Strategy
13 | 
14 | 	// Other options for implementations of the interface
15 | 	// can be stored in a context
16 | 	Context context.Context
17 | 	// Logger is the underline logger
18 | 	Logger logger.Logger
19 | }
20 | 
21 | type SelectOptions struct {
22 | 
23 | 	// Other options for implementations of the interface
24 | 	// can be stored in a context
25 | 	Context  context.Context
26 | 	Strategy Strategy
27 | 
28 | 	Filters []Filter
29 | }
30 | 
31 | type Option func(*Options)
32 | 
33 | // SelectOption used when making a select call.
34 | type SelectOption func(*SelectOptions)
35 | 
36 | // Registry sets the registry used by the selector.
37 | func Registry(r registry.Registry) Option {
38 | 	return func(o *Options) {
39 | 		o.Registry = r
40 | 	}
41 | }
42 | 
43 | // SetStrategy sets the default strategy for the selector.
44 | func SetStrategy(fn Strategy) Option {
45 | 	return func(o *Options) {
46 | 		o.Strategy = fn
47 | 	}
48 | }
49 | 
50 | // WithFilter adds a filter function to the list of filters
51 | // used during the Select call.
52 | func WithFilter(fn ...Filter) SelectOption {
53 | 	return func(o *SelectOptions) {
54 | 		o.Filters = append(o.Filters, fn...)
55 | 	}
56 | }
57 | 
58 | // Strategy sets the selector strategy.
59 | func WithStrategy(fn Strategy) SelectOption {
60 | 	return func(o *SelectOptions) {
61 | 		o.Strategy = fn
62 | 	}
63 | }
64 | 
65 | // WithLogger sets the underline logger.
66 | func WithLogger(l logger.Logger) Option {
67 | 	return func(o *Options) {
68 | 		o.Logger = l
69 | 	}
70 | }
71 | 


--------------------------------------------------------------------------------
/selector/selector.go:
--------------------------------------------------------------------------------
 1 | // Package selector is a way to pick a list of service nodes
 2 | package selector
 3 | 
 4 | import (
 5 | 	"errors"
 6 | 
 7 | 	"go-micro.dev/v5/registry"
 8 | )
 9 | 
10 | // Selector builds on the registry as a mechanism to pick nodes
11 | // and mark their status. This allows host pools and other things
12 | // to be built using various algorithms.
13 | type Selector interface {
14 | 	Init(opts ...Option) error
15 | 	Options() Options
16 | 	// Select returns a function which should return the next node
17 | 	Select(service string, opts ...SelectOption) (Next, error)
18 | 	// Mark sets the success/error against a node
19 | 	Mark(service string, node *registry.Node, err error)
20 | 	// Reset returns state back to zero for a service
21 | 	Reset(service string)
22 | 	// Close renders the selector unusable
23 | 	Close() error
24 | 	// Name of the selector
25 | 	String() string
26 | }
27 | 
28 | // Next is a function that returns the next node
29 | // based on the selector's strategy.
30 | type Next func() (*registry.Node, error)
31 | 
32 | // Filter is used to filter a service during the selection process.
33 | type Filter func([]*registry.Service) []*registry.Service
34 | 
35 | // Strategy is a selection strategy e.g random, round robin.
36 | type Strategy func([]*registry.Service) Next
37 | 
38 | var (
39 | 	DefaultSelector = NewSelector()
40 | 
41 | 	ErrNotFound      = errors.New("not found")
42 | 	ErrNoneAvailable = errors.New("none available")
43 | )
44 | 


--------------------------------------------------------------------------------
/selector/strategy.go:
--------------------------------------------------------------------------------
 1 | package selector
 2 | 
 3 | import (
 4 | 	"math/rand"
 5 | 	"sync"
 6 | 	"time"
 7 | 
 8 | 	"go-micro.dev/v5/registry"
 9 | )
10 | 
11 | func init() {
12 | 	rand.Seed(time.Now().UnixNano())
13 | }
14 | 
15 | // Random is a random strategy algorithm for node selection.
16 | func Random(services []*registry.Service) Next {
17 | 	nodes := make([]*registry.Node, 0, len(services))
18 | 
19 | 	for _, service := range services {
20 | 		nodes = append(nodes, service.Nodes...)
21 | 	}
22 | 
23 | 	return func() (*registry.Node, error) {
24 | 		if len(nodes) == 0 {
25 | 			return nil, ErrNoneAvailable
26 | 		}
27 | 
28 | 		i := rand.Int() % len(nodes)
29 | 		return nodes[i], nil
30 | 	}
31 | }
32 | 
33 | // RoundRobin is a roundrobin strategy algorithm for node selection.
34 | func RoundRobin(services []*registry.Service) Next {
35 | 	nodes := make([]*registry.Node, 0, len(services))
36 | 
37 | 	for _, service := range services {
38 | 		nodes = append(nodes, service.Nodes...)
39 | 	}
40 | 
41 | 	var i = rand.Int()
42 | 	var mtx sync.Mutex
43 | 
44 | 	return func() (*registry.Node, error) {
45 | 		if len(nodes) == 0 {
46 | 			return nil, ErrNoneAvailable
47 | 		}
48 | 
49 | 		mtx.Lock()
50 | 		node := nodes[i%len(nodes)]
51 | 		i++
52 | 		mtx.Unlock()
53 | 
54 | 		return node, nil
55 | 	}
56 | }
57 | 


--------------------------------------------------------------------------------
/selector/strategy_test.go:
--------------------------------------------------------------------------------
 1 | package selector
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"testing"
 6 | 
 7 | 	"go-micro.dev/v5/registry"
 8 | )
 9 | 
10 | func TestStrategies(t *testing.T) {
11 | 	testData := []*registry.Service{
12 | 		{
13 | 			Name:    "test1",
14 | 			Version: "latest",
15 | 			Nodes: []*registry.Node{
16 | 				{
17 | 					Id:      "test1-1",
18 | 					Address: "10.0.0.1:1001",
19 | 				},
20 | 				{
21 | 					Id:      "test1-2",
22 | 					Address: "10.0.0.2:1002",
23 | 				},
24 | 			},
25 | 		},
26 | 		{
27 | 			Name:    "test1",
28 | 			Version: "default",
29 | 			Nodes: []*registry.Node{
30 | 				{
31 | 					Id:      "test1-3",
32 | 					Address: "10.0.0.3:1003",
33 | 				},
34 | 				{
35 | 					Id:      "test1-4",
36 | 					Address: "10.0.0.4:1004",
37 | 				},
38 | 			},
39 | 		},
40 | 	}
41 | 
42 | 	for name, strategy := range map[string]Strategy{"random": Random, "roundrobin": RoundRobin} {
43 | 		next := strategy(testData)
44 | 		counts := make(map[string]int)
45 | 
46 | 		for i := 0; i < 100; i++ {
47 | 			node, err := next()
48 | 			if err != nil {
49 | 				t.Fatal(err)
50 | 			}
51 | 			counts[node.Id]++
52 | 		}
53 | 
54 | 		if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
55 | 			t.Logf("%s: %+v\n", name, counts)
56 | 		}
57 | 	}
58 | }
59 | 


--------------------------------------------------------------------------------
/server/context.go:
--------------------------------------------------------------------------------
 1 | package server
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"sync"
 6 | )
 7 | 
 8 | type serverKey struct{}
 9 | type wgKey struct{}
10 | 
11 | func wait(ctx context.Context) *sync.WaitGroup {
12 | 	if ctx == nil {
13 | 		return nil
14 | 	}
15 | 	wg, ok := ctx.Value(wgKey{}).(*sync.WaitGroup)
16 | 	if !ok {
17 | 		return nil
18 | 	}
19 | 	return wg
20 | }
21 | 
22 | func FromContext(ctx context.Context) (Server, bool) {
23 | 	c, ok := ctx.Value(serverKey{}).(Server)
24 | 	return c, ok
25 | }
26 | 
27 | func NewContext(ctx context.Context, s Server) context.Context {
28 | 	return context.WithValue(ctx, serverKey{}, s)
29 | }
30 | 


--------------------------------------------------------------------------------
/server/extractor_test.go:
--------------------------------------------------------------------------------
 1 | package server
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"reflect"
 6 | 	"testing"
 7 | 
 8 | 	"go-micro.dev/v5/registry"
 9 | )
10 | 
11 | type testHandler struct{}
12 | 
13 | type testRequest struct{}
14 | 
15 | type testResponse struct{}
16 | 
17 | func (t *testHandler) Test(ctx context.Context, req *testRequest, rsp *testResponse) error {
18 | 	return nil
19 | }
20 | 
21 | func TestExtractEndpoint(t *testing.T) {
22 | 	handler := &testHandler{}
23 | 	typ := reflect.TypeOf(handler)
24 | 
25 | 	var endpoints []*registry.Endpoint
26 | 
27 | 	for m := 0; m < typ.NumMethod(); m++ {
28 | 		if e := extractEndpoint(typ.Method(m)); e != nil {
29 | 			endpoints = append(endpoints, e)
30 | 		}
31 | 	}
32 | 
33 | 	if i := len(endpoints); i != 1 {
34 | 		t.Errorf("Expected 1 endpoint, have %d", i)
35 | 	}
36 | 
37 | 	if endpoints[0].Name != "Test" {
38 | 		t.Errorf("Expected handler Test, got %s", endpoints[0].Name)
39 | 	}
40 | 
41 | 	if endpoints[0].Request == nil {
42 | 		t.Error("Expected non nil request")
43 | 	}
44 | 
45 | 	if endpoints[0].Response == nil {
46 | 		t.Error("Expected non nil request")
47 | 	}
48 | 
49 | 	if endpoints[0].Request.Name != "testRequest" {
50 | 		t.Errorf("Expected testRequest got %s", endpoints[0].Request.Name)
51 | 	}
52 | 
53 | 	if endpoints[0].Response.Name != "testResponse" {
54 | 		t.Errorf("Expected testResponse got %s", endpoints[0].Response.Name)
55 | 	}
56 | 
57 | 	if endpoints[0].Request.Type != "testRequest" {
58 | 		t.Errorf("Expected testRequest type got %s", endpoints[0].Request.Type)
59 | 	}
60 | 
61 | 	if endpoints[0].Response.Type != "testResponse" {
62 | 		t.Errorf("Expected testResponse type got %s", endpoints[0].Response.Type)
63 | 	}
64 | }
65 | 


--------------------------------------------------------------------------------
/server/grpc/context.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/server"
 7 | )
 8 | 
 9 | func setServerOption(k, v interface{}) server.Option {
10 | 	return func(o *server.Options) {
11 | 		if o.Context == nil {
12 | 			o.Context = context.Background()
13 | 		}
14 | 		o.Context = context.WithValue(o.Context, k, v)
15 | 	}
16 | }
17 | 


--------------------------------------------------------------------------------
/server/grpc/error.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"net/http"
 5 | 
 6 | 	"go-micro.dev/v5/errors"
 7 | 	"google.golang.org/grpc/codes"
 8 | )
 9 | 
10 | func microError(err *errors.Error) codes.Code {
11 | 	switch err {
12 | 	case nil:
13 | 		return codes.OK
14 | 	}
15 | 
16 | 	switch err.Code {
17 | 	case http.StatusOK:
18 | 		return codes.OK
19 | 	case http.StatusBadRequest:
20 | 		return codes.InvalidArgument
21 | 	case http.StatusRequestTimeout:
22 | 		return codes.DeadlineExceeded
23 | 	case http.StatusNotFound:
24 | 		return codes.NotFound
25 | 	case http.StatusConflict:
26 | 		return codes.AlreadyExists
27 | 	case http.StatusForbidden:
28 | 		return codes.PermissionDenied
29 | 	case http.StatusUnauthorized:
30 | 		return codes.Unauthenticated
31 | 	case http.StatusPreconditionFailed:
32 | 		return codes.FailedPrecondition
33 | 	case http.StatusNotImplemented:
34 | 		return codes.Unimplemented
35 | 	case http.StatusInternalServerError:
36 | 		return codes.Internal
37 | 	case http.StatusServiceUnavailable:
38 | 		return codes.Unavailable
39 | 	}
40 | 
41 | 	return codes.Unknown
42 | }
43 | 


--------------------------------------------------------------------------------
/server/grpc/extractor_test.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"reflect"
 6 | 	"testing"
 7 | 
 8 | 	"go-micro.dev/v5/registry"
 9 | )
10 | 
11 | type testHandler struct{}
12 | 
13 | type testRequest struct{}
14 | 
15 | type testResponse struct{}
16 | 
17 | func (t *testHandler) Test(ctx context.Context, req *testRequest, rsp *testResponse) error {
18 | 	return nil
19 | }
20 | 
21 | func TestExtractEndpoint(t *testing.T) {
22 | 	handler := &testHandler{}
23 | 	typ := reflect.TypeOf(handler)
24 | 
25 | 	var endpoints []*registry.Endpoint
26 | 
27 | 	for m := 0; m < typ.NumMethod(); m++ {
28 | 		if e := extractEndpoint(typ.Method(m)); e != nil {
29 | 			endpoints = append(endpoints, e)
30 | 		}
31 | 	}
32 | 
33 | 	if i := len(endpoints); i != 1 {
34 | 		t.Errorf("Expected 1 endpoint, have %d", i)
35 | 	}
36 | 
37 | 	if endpoints[0].Name != "Test" {
38 | 		t.Errorf("Expected handler Test, got %s", endpoints[0].Name)
39 | 	}
40 | 
41 | 	if endpoints[0].Request == nil {
42 | 		t.Error("Expected non nil request")
43 | 	}
44 | 
45 | 	if endpoints[0].Response == nil {
46 | 		t.Error("Expected non nil request")
47 | 	}
48 | 
49 | 	if endpoints[0].Request.Name != "testRequest" {
50 | 		t.Errorf("Expected testRequest got %s", endpoints[0].Request.Name)
51 | 	}
52 | 
53 | 	if endpoints[0].Response.Name != "testResponse" {
54 | 		t.Errorf("Expected testResponse got %s", endpoints[0].Response.Name)
55 | 	}
56 | 
57 | 	if endpoints[0].Request.Type != "testRequest" {
58 | 		t.Errorf("Expected testRequest type got %s", endpoints[0].Request.Type)
59 | 	}
60 | 
61 | 	if endpoints[0].Response.Type != "testResponse" {
62 | 		t.Errorf("Expected testResponse type got %s", endpoints[0].Response.Type)
63 | 	}
64 | }
65 | 


--------------------------------------------------------------------------------
/server/grpc/handler.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 
 6 | 	"go-micro.dev/v5/registry"
 7 | 	"go-micro.dev/v5/server"
 8 | )
 9 | 
10 | type rpcHandler struct {
11 | 	name      string
12 | 	handler   interface{}
13 | 	endpoints []*registry.Endpoint
14 | 	opts      server.HandlerOptions
15 | }
16 | 
17 | func newRpcHandler(handler interface{}, opts ...server.HandlerOption) server.Handler {
18 | 	options := server.HandlerOptions{
19 | 		Metadata: make(map[string]map[string]string),
20 | 	}
21 | 
22 | 	for _, o := range opts {
23 | 		o(&options)
24 | 	}
25 | 
26 | 	typ := reflect.TypeOf(handler)
27 | 	hdlr := reflect.ValueOf(handler)
28 | 	name := reflect.Indirect(hdlr).Type().Name()
29 | 
30 | 	var endpoints []*registry.Endpoint
31 | 
32 | 	for m := 0; m < typ.NumMethod(); m++ {
33 | 		if e := extractEndpoint(typ.Method(m)); e != nil {
34 | 			e.Name = name + "." + e.Name
35 | 
36 | 			for k, v := range options.Metadata[e.Name] {
37 | 				e.Metadata[k] = v
38 | 			}
39 | 
40 | 			endpoints = append(endpoints, e)
41 | 		}
42 | 	}
43 | 
44 | 	return &rpcHandler{
45 | 		name:      name,
46 | 		handler:   handler,
47 | 		endpoints: endpoints,
48 | 		opts:      options,
49 | 	}
50 | }
51 | 
52 | func (r *rpcHandler) Name() string {
53 | 	return r.name
54 | }
55 | 
56 | func (r *rpcHandler) Handler() interface{} {
57 | 	return r.handler
58 | }
59 | 
60 | func (r *rpcHandler) Endpoints() []*registry.Endpoint {
61 | 	return r.endpoints
62 | }
63 | 
64 | func (r *rpcHandler) Options() server.HandlerOptions {
65 | 	return r.opts
66 | }
67 | 


--------------------------------------------------------------------------------
/server/grpc/response.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/codec"
 5 | )
 6 | 
 7 | type rpcResponse struct {
 8 | 	header map[string]string
 9 | 	codec  codec.Codec
10 | }
11 | 
12 | func (r *rpcResponse) Codec() codec.Writer {
13 | 	return r.codec
14 | }
15 | 
16 | func (r *rpcResponse) WriteHeader(hdr map[string]string) {
17 | 	for k, v := range hdr {
18 | 		r.header[k] = v
19 | 	}
20 | }
21 | 
22 | func (r *rpcResponse) Write(b []byte) error {
23 | 	return r.codec.Write(&codec.Message{
24 | 		Header: r.header,
25 | 		Body:   b,
26 | 	}, nil)
27 | }
28 | 


--------------------------------------------------------------------------------
/server/grpc/stream.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/server"
 7 | 	"google.golang.org/grpc"
 8 | )
 9 | 
10 | // rpcStream implements a server side Stream.
11 | type rpcStream struct {
12 | 	s       grpc.ServerStream
13 | 	request server.Request
14 | }
15 | 
16 | func (r *rpcStream) Close() error {
17 | 	return nil
18 | }
19 | 
20 | func (r *rpcStream) Error() error {
21 | 	return nil
22 | }
23 | 
24 | func (r *rpcStream) Request() server.Request {
25 | 	return r.request
26 | }
27 | 
28 | func (r *rpcStream) Context() context.Context {
29 | 	return r.s.Context()
30 | }
31 | 
32 | func (r *rpcStream) Send(m interface{}) error {
33 | 	return r.s.SendMsg(m)
34 | }
35 | 
36 | func (r *rpcStream) Recv(m interface{}) error {
37 | 	return r.s.RecvMsg(m)
38 | }
39 | 


--------------------------------------------------------------------------------
/server/grpc/util.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"io"
 6 | 	"os"
 7 | 	"sync"
 8 | 
 9 | 	"google.golang.org/grpc/codes"
10 | )
11 | 
12 | // convertCode converts a standard Go error into its canonical code. Note that
13 | // this is only used to translate the error returned by the server applications.
14 | func convertCode(err error) codes.Code {
15 | 	switch err {
16 | 	case nil:
17 | 		return codes.OK
18 | 	case io.EOF:
19 | 		return codes.OutOfRange
20 | 	case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
21 | 		return codes.FailedPrecondition
22 | 	case os.ErrInvalid:
23 | 		return codes.InvalidArgument
24 | 	case context.Canceled:
25 | 		return codes.Canceled
26 | 	case context.DeadlineExceeded:
27 | 		return codes.DeadlineExceeded
28 | 	}
29 | 	switch {
30 | 	case os.IsExist(err):
31 | 		return codes.AlreadyExists
32 | 	case os.IsNotExist(err):
33 | 		return codes.NotFound
34 | 	case os.IsPermission(err):
35 | 		return codes.PermissionDenied
36 | 	}
37 | 	return codes.Unknown
38 | }
39 | 
40 | func wait(ctx context.Context) *sync.WaitGroup {
41 | 	if ctx == nil {
42 | 		return nil
43 | 	}
44 | 	wg, ok := ctx.Value("wait").(*sync.WaitGroup)
45 | 	if !ok {
46 | 		return nil
47 | 	}
48 | 	return wg
49 | }
50 | 


--------------------------------------------------------------------------------
/server/mock/mock_handler.go:
--------------------------------------------------------------------------------
 1 | package mock
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/registry"
 5 | 	"go-micro.dev/v5/server"
 6 | )
 7 | 
 8 | type MockHandler struct {
 9 | 	Opts server.HandlerOptions
10 | 	Hdlr interface{}
11 | 	Id   string
12 | }
13 | 
14 | func (m *MockHandler) Name() string {
15 | 	return m.Id
16 | }
17 | 
18 | func (m *MockHandler) Handler() interface{} {
19 | 	return m.Hdlr
20 | }
21 | 
22 | func (m *MockHandler) Endpoints() []*registry.Endpoint {
23 | 	return []*registry.Endpoint{}
24 | }
25 | 
26 | func (m *MockHandler) Options() server.HandlerOptions {
27 | 	return m.Opts
28 | }
29 | 


--------------------------------------------------------------------------------
/server/mock/mock_subscriber.go:
--------------------------------------------------------------------------------
 1 | package mock
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/registry"
 5 | 	"go-micro.dev/v5/server"
 6 | )
 7 | 
 8 | type MockSubscriber struct {
 9 | 	Opts server.SubscriberOptions
10 | 	Sub  interface{}
11 | 	Id   string
12 | }
13 | 
14 | func (m *MockSubscriber) Topic() string {
15 | 	return m.Id
16 | }
17 | 
18 | func (m *MockSubscriber) Subscriber() interface{} {
19 | 	return m.Sub
20 | }
21 | 
22 | func (m *MockSubscriber) Endpoints() []*registry.Endpoint {
23 | 	return []*registry.Endpoint{}
24 | }
25 | 
26 | func (m *MockSubscriber) Options() server.SubscriberOptions {
27 | 	return m.Opts
28 | }
29 | 


--------------------------------------------------------------------------------
/server/mock/mock_test.go:
--------------------------------------------------------------------------------
 1 | package mock
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"go-micro.dev/v5/server"
 7 | )
 8 | 
 9 | func TestMockServer(t *testing.T) {
10 | 	srv := NewServer(
11 | 		server.Name("mock"),
12 | 		server.Version("latest"),
13 | 	)
14 | 
15 | 	if srv.Options().Name != "mock" {
16 | 		t.Fatalf("Expected name mock, got %s", srv.Options().Name)
17 | 	}
18 | 
19 | 	if srv.Options().Version != "latest" {
20 | 		t.Fatalf("Expected version latest, got %s", srv.Options().Version)
21 | 	}
22 | 
23 | 	srv.Init(server.Version("test"))
24 | 	if srv.Options().Version != "test" {
25 | 		t.Fatalf("Expected version test, got %s", srv.Options().Version)
26 | 	}
27 | 
28 | 	h := srv.NewHandler(func() string { return "foo" })
29 | 	if err := srv.Handle(h); err != nil {
30 | 		t.Fatal(err)
31 | 	}
32 | 
33 | 	sub := srv.NewSubscriber("test", func() string { return "foo" })
34 | 	if err := srv.Subscribe(sub); err != nil {
35 | 		t.Fatal(err)
36 | 	}
37 | 
38 | 	if sub.Topic() != "test" {
39 | 		t.Fatalf("Expected topic test got %s", sub.Topic())
40 | 	}
41 | 
42 | 	if err := srv.Start(); err != nil {
43 | 		t.Fatal(err)
44 | 	}
45 | 
46 | 	if err := srv.Register(); err != nil {
47 | 		t.Fatal(err)
48 | 	}
49 | 
50 | 	if err := srv.Deregister(); err != nil {
51 | 		t.Fatal(err)
52 | 	}
53 | 
54 | 	if err := srv.Stop(); err != nil {
55 | 		t.Fatal(err)
56 | 	}
57 | }
58 | 


--------------------------------------------------------------------------------
/server/proto/server.proto:
--------------------------------------------------------------------------------
 1 | syntax = "proto3";
 2 | 
 3 | package go.micro.server;
 4 | 
 5 | service Server {
 6 | 	rpc Handle(HandleRequest) returns (HandleResponse) {};
 7 | 	rpc Subscribe(SubscribeRequest) returns (SubscribeResponse) {};
 8 | }
 9 | 
10 | message HandleRequest {
11 | 	string service = 1;
12 | 	string endpoint = 2;
13 | 	string protocol = 3;
14 | }
15 | 
16 | message HandleResponse {}
17 | 
18 | message SubscribeRequest {
19 | 	string topic = 1;
20 | }
21 | 
22 | message SubscribeResponse {}
23 | 


--------------------------------------------------------------------------------
/server/rpc_event.go:
--------------------------------------------------------------------------------
 1 | package server
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/broker"
 5 | 	"go-micro.dev/v5/transport"
 6 | 	"go-micro.dev/v5/transport/headers"
 7 | )
 8 | 
 9 | // event is a broker event we handle on the server transport.
10 | type event struct {
11 | 	err     error
12 | 	message *broker.Message
13 | }
14 | 
15 | func (e *event) Ack() error {
16 | 	// there is no ack support
17 | 	return nil
18 | }
19 | 
20 | func (e *event) Message() *broker.Message {
21 | 	return e.message
22 | }
23 | 
24 | func (e *event) Error() error {
25 | 	return e.err
26 | }
27 | 
28 | func (e *event) Topic() string {
29 | 	return e.message.Header[headers.Message]
30 | }
31 | 
32 | func newEvent(msg transport.Message) *event {
33 | 	return &event{
34 | 		message: &broker.Message{
35 | 			Header: msg.Header,
36 | 			Body:   msg.Body,
37 | 		},
38 | 	}
39 | }
40 | 


--------------------------------------------------------------------------------
/server/rpc_handler.go:
--------------------------------------------------------------------------------
 1 | package server
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 
 6 | 	"go-micro.dev/v5/registry"
 7 | )
 8 | 
 9 | type RpcHandler struct {
10 | 	handler   interface{}
11 | 	opts      HandlerOptions
12 | 	name      string
13 | 	endpoints []*registry.Endpoint
14 | }
15 | 
16 | func NewRpcHandler(handler interface{}, opts ...HandlerOption) Handler {
17 | 	options := HandlerOptions{
18 | 		Metadata: make(map[string]map[string]string),
19 | 	}
20 | 
21 | 	for _, o := range opts {
22 | 		o(&options)
23 | 	}
24 | 
25 | 	typ := reflect.TypeOf(handler)
26 | 	hdlr := reflect.ValueOf(handler)
27 | 	name := reflect.Indirect(hdlr).Type().Name()
28 | 
29 | 	var endpoints []*registry.Endpoint
30 | 
31 | 	for m := 0; m < typ.NumMethod(); m++ {
32 | 		if e := extractEndpoint(typ.Method(m)); e != nil {
33 | 			e.Name = name + "." + e.Name
34 | 
35 | 			for k, v := range options.Metadata[e.Name] {
36 | 				e.Metadata[k] = v
37 | 			}
38 | 
39 | 			endpoints = append(endpoints, e)
40 | 		}
41 | 	}
42 | 
43 | 	return &RpcHandler{
44 | 		name:      name,
45 | 		handler:   handler,
46 | 		endpoints: endpoints,
47 | 		opts:      options,
48 | 	}
49 | }
50 | 
51 | func (r *RpcHandler) Name() string {
52 | 	return r.name
53 | }
54 | 
55 | func (r *RpcHandler) Handler() interface{} {
56 | 	return r.handler
57 | }
58 | 
59 | func (r *RpcHandler) Endpoints() []*registry.Endpoint {
60 | 	return r.endpoints
61 | }
62 | 
63 | func (r *RpcHandler) Options() HandlerOptions {
64 | 	return r.opts
65 | }
66 | 


--------------------------------------------------------------------------------
/server/rpc_response.go:
--------------------------------------------------------------------------------
 1 | package server
 2 | 
 3 | import (
 4 | 	"net/http"
 5 | 
 6 | 	"go-micro.dev/v5/codec"
 7 | 	"go-micro.dev/v5/transport"
 8 | )
 9 | 
10 | type rpcResponse struct {
11 | 	header map[string]string
12 | 	socket transport.Socket
13 | 	codec  codec.Codec
14 | }
15 | 
16 | func (r *rpcResponse) Codec() codec.Writer {
17 | 	return r.codec
18 | }
19 | 
20 | func (r *rpcResponse) WriteHeader(hdr map[string]string) {
21 | 	for k, v := range hdr {
22 | 		r.header[k] = v
23 | 	}
24 | }
25 | 
26 | func (r *rpcResponse) Write(b []byte) error {
27 | 	if _, ok := r.header["Content-Type"]; !ok {
28 | 		r.header["Content-Type"] = http.DetectContentType(b)
29 | 	}
30 | 
31 | 	return r.socket.Send(&transport.Message{
32 | 		Header: r.header,
33 | 		Body:   b,
34 | 	})
35 | }
36 | 


--------------------------------------------------------------------------------
/server/rpc_util.go:
--------------------------------------------------------------------------------
 1 | package server
 2 | 
 3 | import (
 4 | 	"sync"
 5 | )
 6 | 
 7 | // waitgroup for global management of connections.
 8 | type waitGroup struct {
 9 | 	// global waitgroup
10 | 	gg *sync.WaitGroup
11 | 	// local waitgroup
12 | 	lg sync.WaitGroup
13 | }
14 | 
15 | // NewWaitGroup returns a new double waitgroup for global management of processes.
16 | func NewWaitGroup(gWg *sync.WaitGroup) *waitGroup {
17 | 	return &waitGroup{
18 | 		gg: gWg,
19 | 	}
20 | }
21 | 
22 | func (w *waitGroup) Add(i int) {
23 | 	w.lg.Add(i)
24 | 	if w.gg != nil {
25 | 		w.gg.Add(i)
26 | 	}
27 | }
28 | 
29 | func (w *waitGroup) Done() {
30 | 	w.lg.Done()
31 | 	if w.gg != nil {
32 | 		w.gg.Done()
33 | 	}
34 | }
35 | 
36 | func (w *waitGroup) Wait() {
37 | 	// only wait on local group
38 | 	w.lg.Wait()
39 | }
40 | 


--------------------------------------------------------------------------------
/server/wrapper.go:
--------------------------------------------------------------------------------
 1 | package server
 2 | 
 3 | import (
 4 | 	"context"
 5 | )
 6 | 
 7 | // HandlerFunc represents a single method of a handler. It's used primarily
 8 | // for the wrappers. What's handed to the actual method is the concrete
 9 | // request and response types.
10 | type HandlerFunc func(ctx context.Context, req Request, rsp interface{}) error
11 | 
12 | // SubscriberFunc represents a single method of a subscriber. It's used primarily
13 | // for the wrappers. What's handed to the actual method is the concrete
14 | // publication message.
15 | type SubscriberFunc func(ctx context.Context, msg Message) error
16 | 
17 | // HandlerWrapper wraps the HandlerFunc and returns the equivalent.
18 | type HandlerWrapper func(HandlerFunc) HandlerFunc
19 | 
20 | // SubscriberWrapper wraps the SubscriberFunc and returns the equivalent.
21 | type SubscriberWrapper func(SubscriberFunc) SubscriberFunc
22 | 
23 | // StreamWrapper wraps a Stream interface and returns the equivalent.
24 | // Because streams exist for the lifetime of a method invocation this
25 | // is a convenient way to wrap a Stream as its in use for trace, monitoring,
26 | // metrics, etc.
27 | type StreamWrapper func(Stream) Stream
28 | 


--------------------------------------------------------------------------------
/store/mysql/mysql_test.go:
--------------------------------------------------------------------------------
 1 | //go:build integration
 2 | // +build integration
 3 | 
 4 | package mysql
 5 | 
 6 | import (
 7 | 	"encoding/json"
 8 | 	"os"
 9 | 	"testing"
10 | 	"time"
11 | 
12 | 	_ "github.com/go-sql-driver/mysql"
13 | 	"go-micro.dev/v5/store"
14 | )
15 | 
16 | var (
17 | 	sqlStoreT store.Store
18 | )
19 | 
20 | func TestMain(m *testing.M) {
21 | 	if tr := os.Getenv("TRAVIS"); len(tr) > 0 {
22 | 		os.Exit(0)
23 | 	}
24 | 
25 | 	sqlStoreT = NewMysqlStore(
26 | 		store.Database("testMicro"),
27 | 		store.Nodes("root:123@(127.0.0.1:3306)/test?charset=utf8&parseTime=true&loc=Asia%2FShanghai"),
28 | 	)
29 | 	os.Exit(m.Run())
30 | }
31 | 
32 | func TestWrite(t *testing.T) {
33 | 	err := sqlStoreT.Write(
34 | 		&store.Record{
35 | 			Key:    "test",
36 | 			Value:  []byte("foo2"),
37 | 			Expiry: time.Second * 200,
38 | 		},
39 | 	)
40 | 	if err != nil {
41 | 		t.Error(err)
42 | 	}
43 | }
44 | 
45 | func TestDelete(t *testing.T) {
46 | 	err := sqlStoreT.Delete("test")
47 | 	if err != nil {
48 | 		t.Error(err)
49 | 	}
50 | }
51 | 
52 | func TestRead(t *testing.T) {
53 | 	records, err := sqlStoreT.Read("test")
54 | 	if err != nil {
55 | 		t.Error(err)
56 | 	}
57 | 
58 | 	t.Log(string(records[0].Value))
59 | }
60 | 
61 | func TestList(t *testing.T) {
62 | 	records, err := sqlStoreT.List()
63 | 	if err != nil {
64 | 		t.Error(err)
65 | 	} else {
66 | 		beauty, _ := json.Marshal(records)
67 | 		t.Log(string(beauty))
68 | 	}
69 | }
70 | 


--------------------------------------------------------------------------------
/store/nats-js-kv/context.go:
--------------------------------------------------------------------------------
 1 | package natsjskv
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"go-micro.dev/v5/store"
 7 | )
 8 | 
 9 | // setStoreOption returns a function to setup a context with given value.
10 | func setStoreOption(k, v interface{}) store.Option {
11 | 	return func(o *store.Options) {
12 | 		if o.Context == nil {
13 | 			o.Context = context.Background()
14 | 		}
15 | 
16 | 		o.Context = context.WithValue(o.Context, k, v)
17 | 	}
18 | }
19 | 


--------------------------------------------------------------------------------
/store/noop.go:
--------------------------------------------------------------------------------
 1 | package store
 2 | 
 3 | type noopStore struct{}
 4 | 
 5 | func (n *noopStore) Init(opts ...Option) error {
 6 | 	return nil
 7 | }
 8 | 
 9 | func (n *noopStore) Options() Options {
10 | 	return Options{}
11 | }
12 | 
13 | func (n *noopStore) String() string {
14 | 	return "noop"
15 | }
16 | 
17 | func (n *noopStore) Read(key string, opts ...ReadOption) ([]*Record, error) {
18 | 	return []*Record{}, nil
19 | }
20 | 
21 | func (n *noopStore) Write(r *Record, opts ...WriteOption) error {
22 | 	return nil
23 | }
24 | 
25 | func (n *noopStore) Delete(key string, opts ...DeleteOption) error {
26 | 	return nil
27 | }
28 | 
29 | func (n *noopStore) List(opts ...ListOption) ([]string, error) {
30 | 	return []string{}, nil
31 | }
32 | 
33 | func (n *noopStore) Close() error {
34 | 	return nil
35 | }
36 | 
37 | func NewNoopStore(opts ...Option) Store {
38 | 	return new(noopStore)
39 | }
40 | 


--------------------------------------------------------------------------------
/store/postgres/README.md:
--------------------------------------------------------------------------------
 1 | # Postgres plugin
 2 | 
 3 | This module implements a Postgres implementation of the micro store interface. 
 4 | 
 5 | ## Implementation notes
 6 | 
 7 | ### Concepts
 8 | We maintain a single connection to the Postgres server. Due to the way connections are handled this means that all micro "databases" and "tables" are stored under a single Postgres database as specified in the connection string (https://www.postgresql.org/docs/8.1/ddl-schemas.html). The mapping of micro to Postgres concepts is:
 9 | - micro database => Postgres schema
10 | - micro table => Postgres table
11 | 
12 | ### Expiry
13 | Expiry is managed by an expiry column in the table. A record's expiry is specified in the column and when a record is read the expiry field is first checked, only returning the record if its still valid otherwise it's deleted. A maintenance loop also periodically runs to delete any rows that have expired. 
14 | 


--------------------------------------------------------------------------------
/store/postgres/pgx/README.md:
--------------------------------------------------------------------------------
 1 | # Postgres pgx plugin
 2 | 
 3 | This module implements a Postgres implementation of the micro store interface. 
 4 | It uses modern https://github.com/jackc/pgx driver to access Postgres.
 5 | 
 6 | ## Implementation notes
 7 | 
 8 | ### Concepts
 9 | Every database has they own connection pool. Due to the way connections are handled this means that all micro "databases" and "tables" can be stored under a single or several Postgres database as specified in the connection string (https://www.postgresql.org/docs/8.1/ddl-schemas.html). The mapping of micro to Postgres concepts is:
10 | - micro database => Postgres schema
11 | - micro table => Postgres table
12 | 
13 | ### Expiry
14 | Expiry is managed by an expiry column in the table. A record's expiry is specified in the column and when a record is read the expiry field is first checked, only returning the record if it's still valid otherwise it's deleted. A maintenance loop also periodically runs to delete any rows that have expired. 
15 | 


--------------------------------------------------------------------------------
/store/postgres/pgx/db.go:
--------------------------------------------------------------------------------
1 | package pgx
2 | 
3 | import "github.com/jackc/pgx/v4/pgxpool"
4 | 
5 | type DB struct {
6 | 	conn   *pgxpool.Pool
7 | 	tables map[string]Queries
8 | }
9 | 


--------------------------------------------------------------------------------
/store/postgres/pgx/metadata.go:
--------------------------------------------------------------------------------
 1 | package pgx
 2 | 
 3 | import (
 4 | 	"database/sql/driver"
 5 | 	"encoding/json"
 6 | 	"errors"
 7 | )
 8 | 
 9 | type Metadata map[string]interface{}
10 | 
11 | // Scan satisfies the sql.Scanner interface.
12 | func (m *Metadata) Scan(src interface{}) error {
13 | 	source, ok := src.([]byte)
14 | 	if !ok {
15 | 		return errors.New("type assertion .([]byte) failed")
16 | 	}
17 | 
18 | 	var i interface{}
19 | 	err := json.Unmarshal(source, &i)
20 | 	if err != nil {
21 | 		return err
22 | 	}
23 | 
24 | 	*m, ok = i.(map[string]interface{})
25 | 	if !ok {
26 | 		return errors.New("type assertion .(map[string]interface{}) failed")
27 | 	}
28 | 
29 | 	return nil
30 | }
31 | 
32 | // Value satisfies the driver.Valuer interface.
33 | func (m *Metadata) Value() (driver.Value, error) {
34 | 	j, err := json.Marshal(m)
35 | 	return j, err
36 | }
37 | 
38 | func toMetadata(m *Metadata) map[string]interface{} {
39 | 	md := make(map[string]interface{})
40 | 	for k, v := range *m {
41 | 		md[k] = v
42 | 	}
43 | 	return md
44 | }
45 | 


--------------------------------------------------------------------------------
/store/postgres/pgx/queries.go:
--------------------------------------------------------------------------------
 1 | package pgx
 2 | 
 3 | import "fmt"
 4 | 
 5 | type Queries struct {
 6 | 	// read
 7 | 	ListAsc           string
 8 | 	ListAscLimit      string
 9 | 	ListDesc          string
10 | 	ListDescLimit     string
11 | 	ReadOne           string
12 | 	ReadManyAsc       string
13 | 	ReadManyAscLimit  string
14 | 	ReadManyDesc      string
15 | 	ReadManyDescLimit string
16 | 
17 | 	// change
18 | 	Write         string
19 | 	Delete        string
20 | 	DeleteExpired string
21 | }
22 | 
23 | func NewQueries(database, table string) Queries {
24 | 	return Queries{
25 | 		ListAsc:           fmt.Sprintf(list, database, table) + asc,
26 | 		ListAscLimit:      fmt.Sprintf(list, database, table) + asc + limit,
27 | 		ListDesc:          fmt.Sprintf(list, database, table) + desc,
28 | 		ListDescLimit:     fmt.Sprintf(list, database, table) + desc + limit,
29 | 		ReadOne:           fmt.Sprintf(readOne, database, table),
30 | 		ReadManyAsc:       fmt.Sprintf(readMany, database, table) + asc,
31 | 		ReadManyAscLimit:  fmt.Sprintf(readMany, database, table) + asc + limit,
32 | 		ReadManyDesc:      fmt.Sprintf(readMany, database, table) + desc,
33 | 		ReadManyDescLimit: fmt.Sprintf(readMany, database, table) + desc + limit,
34 | 		Write:             fmt.Sprintf(write, database, table),
35 | 		Delete:            fmt.Sprintf(deleteRecord, database, table),
36 | 		DeleteExpired:     fmt.Sprintf(deleteExpired, database, table),
37 | 	}
38 | }
39 | 


--------------------------------------------------------------------------------
/store/postgres/pgx/templates.go:
--------------------------------------------------------------------------------
 1 | package pgx
 2 | 
 3 | // init
 4 | 
 5 | const createSchema = "CREATE SCHEMA IF NOT EXISTS %s"
 6 | const createTable = `CREATE TABLE IF NOT EXISTS %s.%s
 7 | (
 8 | 	key text primary key,
 9 | 	value bytea,
10 | 	metadata JSONB,
11 | 	expiry timestamp with time zone
12 | )`
13 | const createMDIndex = `create index if not exists idx_md_%s ON %s.%s USING GIN (metadata)`
14 | const createExpiryIndex = `create index if not exists idx_expiry_%s on %s.%s (expiry) where (expiry IS NOT NULL)`
15 | 
16 | // base queries
17 | const (
18 | 	list     = "SELECT key FROM %s.%s WHERE key LIKE $1 and (expiry < now() or expiry isnull)"
19 | 	readOne  = "SELECT key, value, metadata, expiry FROM %s.%s WHERE key = $1 and (expiry < now() or expiry isnull)"
20 | 	readMany = "SELECT key, value, metadata, expiry FROM %s.%s WHERE key LIKE $1 and (expiry < now() or expiry isnull)"
21 | 	write    = `INSERT INTO %s.%s(key, value, metadata, expiry)
22 | VALUES ($1, $2::bytea, $3, $4)
23 | ON CONFLICT (key)
24 | DO UPDATE
25 | SET value = EXCLUDED.value, metadata = EXCLUDED.metadata, expiry = EXCLUDED.expiry`
26 | 	deleteRecord  = "DELETE FROM %s.%s WHERE key = $1"
27 | 	deleteExpired = "DELETE FROM %s.%s WHERE expiry < now()"
28 | )
29 | 
30 | // suffixes
31 | const (
32 | 	limit = " LIMIT $2 OFFSET $3"
33 | 	asc   = " ORDER BY key ASC"
34 | 	desc  = " ORDER BY key DESC"
35 | )
36 | 


--------------------------------------------------------------------------------
/test/benchmark.go:
--------------------------------------------------------------------------------
 1 | package test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"go-micro.dev/v5"
 7 | 	"go-micro.dev/v5/broker"
 8 | 	"go-micro.dev/v5/client"
 9 | 	"go-micro.dev/v5/registry"
10 | 	"go-micro.dev/v5/server"
11 | 	"go-micro.dev/v5/transport"
12 | 	"go-micro.dev/v5/util/test"
13 | )
14 | 
15 | func BenchmarkService(b *testing.B) {
16 | 	cfg := ServiceTestConfig{
17 | 		Name:       "test-service",
18 | 		NewService: newService,
19 | 		Parallel:   []int{1, 8, 16, 32, 64},
20 | 		Sequential: []int{0},
21 | 		Streams:    []int{0},
22 | 		//		PubSub:     []int{10},
23 | 	}
24 | 
25 | 	cfg.Run(b)
26 | }
27 | 
28 | func newService(name string, opts ...micro.Option) (micro.Service, error) {
29 | 	r := registry.NewMemoryRegistry(
30 | 		registry.Services(test.Data),
31 | 	)
32 | 
33 | 	b := broker.NewMemoryBroker()
34 | 
35 | 	t := transport.NewHTTPTransport()
36 | 	c := client.NewClient(
37 | 		client.Transport(t),
38 | 		client.Broker(b),
39 | 	)
40 | 
41 | 	s := server.NewRPCServer(
42 | 		server.Name(name),
43 | 		server.Registry(r),
44 | 		server.Transport(t),
45 | 		server.Broker(b),
46 | 	)
47 | 
48 | 	if err := s.Init(); err != nil {
49 | 		return nil, err
50 | 	}
51 | 
52 | 	options := []micro.Option{
53 | 		micro.Name(name),
54 | 		micro.Server(s),
55 | 		micro.Client(c),
56 | 		micro.Registry(r),
57 | 		micro.Broker(b),
58 | 	}
59 | 	options = append(options, opts...)
60 | 
61 | 	srv := micro.NewService(options...)
62 | 
63 | 	return srv, nil
64 | }
65 | 


--------------------------------------------------------------------------------
/transport/context.go:
--------------------------------------------------------------------------------
 1 | package transport
 2 | 
 3 | import (
 4 | 	"net"
 5 | )
 6 | 
 7 | type netListener struct{}
 8 | 
 9 | // getNetListener Get net.Listener from ListenOptions.
10 | func getNetListener(o *ListenOptions) net.Listener {
11 | 	if o.Context == nil {
12 | 		return nil
13 | 	}
14 | 
15 | 	if l, ok := o.Context.Value(netListener{}).(net.Listener); ok && l != nil {
16 | 		return l
17 | 	}
18 | 
19 | 	return nil
20 | }
21 | 


--------------------------------------------------------------------------------
/transport/grpc/handler.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"runtime/debug"
 5 | 
 6 | 	"go-micro.dev/v5/errors"
 7 | 	"go-micro.dev/v5/logger"
 8 | 	"go-micro.dev/v5/transport"
 9 | 	pb "go-micro.dev/v5/transport/grpc/proto"
10 | 	"google.golang.org/grpc/peer"
11 | )
12 | 
13 | // microTransport satisfies the pb.TransportServer inteface.
14 | type microTransport struct {
15 | 	addr string
16 | 	fn   func(transport.Socket)
17 | }
18 | 
19 | func (m *microTransport) Stream(ts pb.Transport_StreamServer) (err error) {
20 | 	sock := &grpcTransportSocket{
21 | 		stream: ts,
22 | 		local:  m.addr,
23 | 	}
24 | 
25 | 	p, ok := peer.FromContext(ts.Context())
26 | 	if ok {
27 | 		sock.remote = p.Addr.String()
28 | 	}
29 | 
30 | 	defer func() {
31 | 		if r := recover(); r != nil {
32 | 			logger.Error(r, string(debug.Stack()))
33 | 			sock.Close()
34 | 			err = errors.InternalServerError("go.micro.transport", "panic recovered: %v", r)
35 | 		}
36 | 	}()
37 | 
38 | 	// execute socket func
39 | 	m.fn(sock)
40 | 
41 | 	return err
42 | }
43 | 


--------------------------------------------------------------------------------
/transport/grpc/proto/transport.proto:
--------------------------------------------------------------------------------
 1 | syntax = "proto3";
 2 | 
 3 | option go_package = "./proto;transport";
 4 | 
 5 | package go.micro.transport.grpc;
 6 | 
 7 | service Transport {
 8 | 	rpc Stream(stream Message) returns (stream Message) {}
 9 | }
10 | 
11 | message Message {
12 | 	map<string, string> header = 1;
13 | 	bytes body = 2;
14 | }
15 | 


--------------------------------------------------------------------------------
/transport/headers/headers.go:
--------------------------------------------------------------------------------
 1 | // headers is a package for internal micro global constants
 2 | package headers
 3 | 
 4 | const (
 5 | 	// Message header is a header for internal message communication.
 6 | 	Message = "Micro-Topic"
 7 | 	// Request header is a message header for internal request communication.
 8 | 	Request = "Micro-Service"
 9 | 	// Error header contains an error message.
10 | 	Error = "Micro-Error"
11 | 	// Endpoint header.
12 | 	Endpoint = "Micro-Endpoint"
13 | 	// Method header.
14 | 	Method = "Micro-Method"
15 | 	// ID header.
16 | 	ID = "Micro-ID"
17 | 	// Prefix used to prefix headers.
18 | 	Prefix = "Micro-"
19 | 	// Namespace header.
20 | 	Namespace = "Micro-Namespace"
21 | 	// Protocol header.
22 | 	Protocol = "Micro-Protocol"
23 | 	// Target header.
24 | 	Target = "Micro-Target"
25 | 	// ContentType header.
26 | 	ContentType = "Content-Type"
27 | 	// SpanID header.
28 | 	SpanID = "Micro-Span-ID"
29 | 	// TraceIDKey header.
30 | 	TraceIDKey = "Micro-Trace-ID"
31 | 	// Stream header.
32 | 	Stream = "Micro-Stream"
33 | )
34 | 


--------------------------------------------------------------------------------
/transport/http2_buf_pool.go:
--------------------------------------------------------------------------------
 1 | package transport
 2 | 
 3 | import "sync"
 4 | 
 5 | var http2BufPool = sync.Pool{
 6 | 	New: func() interface{} {
 7 | 		return make([]byte, DefaultBufSizeH2)
 8 | 	},
 9 | }
10 | 
11 | func getHTTP2BufPool() *sync.Pool {
12 | 	return &http2BufPool
13 | }
14 | 


--------------------------------------------------------------------------------
/transport/nats/options.go:
--------------------------------------------------------------------------------
 1 | package nats
 2 | 
 3 | import (
 4 | 	"context"
 5 | 
 6 | 	"github.com/nats-io/nats.go"
 7 | 	"go-micro.dev/v5/transport"
 8 | )
 9 | 
10 | type optionsKey struct{}
11 | 
12 | // Options allow to inject a nats.Options struct for configuring
13 | // the nats connection.
14 | func Options(nopts nats.Options) transport.Option {
15 | 	return func(o *transport.Options) {
16 | 		if o.Context == nil {
17 | 			o.Context = context.Background()
18 | 		}
19 | 		o.Context = context.WithValue(o.Context, optionsKey{}, nopts)
20 | 	}
21 | }
22 | 


--------------------------------------------------------------------------------
/transport/transport.go:
--------------------------------------------------------------------------------
 1 | // Package transport is an interface for synchronous connection based communication
 2 | package transport
 3 | 
 4 | import (
 5 | 	"time"
 6 | )
 7 | 
 8 | // Transport is an interface which is used for communication between
 9 | // services. It uses connection based socket send/recv semantics and
10 | // has various implementations; http, grpc, quic.
11 | type Transport interface {
12 | 	Init(...Option) error
13 | 	Options() Options
14 | 	Dial(addr string, opts ...DialOption) (Client, error)
15 | 	Listen(addr string, opts ...ListenOption) (Listener, error)
16 | 	String() string
17 | }
18 | 
19 | // Message is a broker message.
20 | type Message struct {
21 | 	Header map[string]string
22 | 	Body   []byte
23 | }
24 | 
25 | type Socket interface {
26 | 	Recv(*Message) error
27 | 	Send(*Message) error
28 | 	Close() error
29 | 	Local() string
30 | 	Remote() string
31 | }
32 | 
33 | type Client interface {
34 | 	Socket
35 | }
36 | 
37 | type Listener interface {
38 | 	Addr() string
39 | 	Close() error
40 | 	Accept(func(Socket)) error
41 | }
42 | 
43 | type Option func(*Options)
44 | 
45 | type DialOption func(*DialOptions)
46 | 
47 | type ListenOption func(*ListenOptions)
48 | 
49 | var (
50 | 	DefaultTransport Transport = NewHTTPTransport()
51 | 
52 | 	DefaultDialTimeout = time.Second * 5
53 | )
54 | 


--------------------------------------------------------------------------------
/util/backoff/backoff.go:
--------------------------------------------------------------------------------
 1 | // Package backoff provides backoff functionality
 2 | package backoff
 3 | 
 4 | import (
 5 | 	"math"
 6 | 	"time"
 7 | )
 8 | 
 9 | // Do is a function x^e multiplied by a factor of 0.1 second.
10 | // Result is limited to 2 minute.
11 | func Do(attempts int) time.Duration {
12 | 	if attempts > 13 {
13 | 		return 2 * time.Minute
14 | 	}
15 | 	return time.Duration(math.Pow(float64(attempts), math.E)) * time.Millisecond * 100
16 | }
17 | 


--------------------------------------------------------------------------------
/util/buf/buf.go:
--------------------------------------------------------------------------------
 1 | package buf
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | )
 6 | 
 7 | type buffer struct {
 8 | 	*bytes.Buffer
 9 | }
10 | 
11 | func (b *buffer) Close() error {
12 | 	b.Buffer.Reset()
13 | 	return nil
14 | }
15 | 
16 | func New(b *bytes.Buffer) *buffer {
17 | 	if b == nil {
18 | 		b = bytes.NewBuffer(nil)
19 | 	}
20 | 	return &buffer{b}
21 | }
22 | 


--------------------------------------------------------------------------------
/util/grpc/grpc.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"strings"
 6 | )
 7 | 
 8 | // ServiceMethod converts a gRPC method to a Go method
 9 | // Input:
10 | // Foo.Bar, /Foo/Bar, /package.Foo/Bar, /a.package.Foo/Bar
11 | // Output:
12 | // [Foo, Bar].
13 | func ServiceMethod(m string) (string, string, error) {
14 | 	if len(m) == 0 {
15 | 		return "", "", fmt.Errorf("malformed method name: %q", m)
16 | 	}
17 | 
18 | 	// grpc method
19 | 	if m[0] == '/' {
20 | 		// [ , Foo, Bar]
21 | 		// [ , package.Foo, Bar]
22 | 		// [ , a.package.Foo, Bar]
23 | 		parts := strings.Split(m, "/")
24 | 		if len(parts) != 3 || len(parts[1]) == 0 || len(parts[2]) == 0 {
25 | 			return "", "", fmt.Errorf("malformed method name: %q", m)
26 | 		}
27 | 		service := strings.Split(parts[1], ".")
28 | 		return service[len(service)-1], parts[2], nil
29 | 	}
30 | 
31 | 	// non grpc method
32 | 	parts := strings.Split(m, ".")
33 | 
34 | 	// expect [Foo, Bar]
35 | 	if len(parts) != 2 {
36 | 		return "", "", fmt.Errorf("malformed method name: %q", m)
37 | 	}
38 | 
39 | 	return parts[0], parts[1], nil
40 | }
41 | 
42 | // ServiceFromMethod returns the service
43 | // /service.Foo/Bar => service.
44 | func ServiceFromMethod(m string) string {
45 | 	if len(m) == 0 {
46 | 		return m
47 | 	}
48 | 	if m[0] != '/' {
49 | 		return m
50 | 	}
51 | 	parts := strings.Split(m, "/")
52 | 	if len(parts) < 3 {
53 | 		return m
54 | 	}
55 | 	parts = strings.Split(parts[1], ".")
56 | 	return strings.Join(parts[:len(parts)-1], ".")
57 | }
58 | 


--------------------------------------------------------------------------------
/util/grpc/grpc_test.go:
--------------------------------------------------------------------------------
 1 | package grpc
 2 | 
 3 | import (
 4 | 	"testing"
 5 | )
 6 | 
 7 | func TestServiceMethod(t *testing.T) {
 8 | 	type testCase struct {
 9 | 		input   string
10 | 		service string
11 | 		method  string
12 | 		err     bool
13 | 	}
14 | 
15 | 	methods := []testCase{
16 | 		{"Foo.Bar", "Foo", "Bar", false},
17 | 		{"/Foo/Bar", "Foo", "Bar", false},
18 | 		{"/package.Foo/Bar", "Foo", "Bar", false},
19 | 		{"/a.package.Foo/Bar", "Foo", "Bar", false},
20 | 		{"a.package.Foo/Bar", "", "", true},
21 | 		{"/Foo/Bar/Baz", "", "", true},
22 | 		{"Foo.Bar.Baz", "", "", true},
23 | 	}
24 | 	for _, test := range methods {
25 | 		service, method, err := ServiceMethod(test.input)
26 | 		if err != nil && test.err == true {
27 | 			continue
28 | 		}
29 | 		// unexpected error
30 | 		if err != nil && test.err == false {
31 | 			t.Fatalf("unexpected err %v for %+v", err, test)
32 | 		}
33 | 		// expecter error
34 | 		if test.err == true && err == nil {
35 | 			t.Fatalf("expected error for %+v: got service: %s method: %s", test, service, method)
36 | 		}
37 | 
38 | 		if service != test.service {
39 | 			t.Fatalf("wrong service for %+v: got service: %s method: %s", test, service, method)
40 | 		}
41 | 
42 | 		if method != test.method {
43 | 			t.Fatalf("wrong method for %+v: got service: %s method: %s", test, service, method)
44 | 		}
45 | 	}
46 | }
47 | 


--------------------------------------------------------------------------------
/util/http/http_test.go:
--------------------------------------------------------------------------------
 1 | package http
 2 | 
 3 | import (
 4 | 	"io"
 5 | 	"net"
 6 | 	"net/http"
 7 | 	"testing"
 8 | 
 9 | 	"go-micro.dev/v5/registry"
10 | )
11 | 
12 | func TestRoundTripper(t *testing.T) {
13 | 	m := registry.NewMemoryRegistry()
14 | 
15 | 	rt := NewRoundTripper(
16 | 		WithRegistry(m),
17 | 	)
18 | 
19 | 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
20 | 		w.Write([]byte(`hello world`))
21 | 	})
22 | 
23 | 	l, err := net.Listen("tcp", "127.0.0.1:0")
24 | 	if err != nil {
25 | 		t.Fatal(err)
26 | 	}
27 | 	defer l.Close()
28 | 
29 | 	go http.Serve(l, nil)
30 | 
31 | 	m.Register(&registry.Service{
32 | 		Name: "example.com",
33 | 		Nodes: []*registry.Node{
34 | 			{
35 | 				Id:      "1",
36 | 				Address: l.Addr().String(),
37 | 			},
38 | 		},
39 | 	})
40 | 
41 | 	req, err := http.NewRequest("GET", "http://example.com", nil)
42 | 	if err != nil {
43 | 		t.Fatal(err)
44 | 	}
45 | 
46 | 	w, err := rt.RoundTrip(req)
47 | 	if err != nil {
48 | 		t.Fatal(err)
49 | 	}
50 | 
51 | 	b, err := io.ReadAll(w.Body)
52 | 	if err != nil {
53 | 		t.Fatal(err)
54 | 	}
55 | 	w.Body.Close()
56 | 
57 | 	if string(b) != "hello world" {
58 | 		t.Fatal("response is", string(b))
59 | 	}
60 | 
61 | 	// test http request
62 | 	c := &http.Client{
63 | 		Transport: rt,
64 | 	}
65 | 
66 | 	rsp, err := c.Get("http://example.com")
67 | 	if err != nil {
68 | 		t.Fatal(err)
69 | 	}
70 | 
71 | 	b, err = io.ReadAll(rsp.Body)
72 | 	if err != nil {
73 | 		t.Fatal(err)
74 | 	}
75 | 	rsp.Body.Close()
76 | 
77 | 	if string(b) != "hello world" {
78 | 		t.Fatal("response is", string(b))
79 | 	}
80 | }
81 | 


--------------------------------------------------------------------------------
/util/http/options.go:
--------------------------------------------------------------------------------
 1 | package http
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/registry"
 5 | )
 6 | 
 7 | type Options struct {
 8 | 	Registry registry.Registry
 9 | }
10 | 
11 | type Option func(*Options)
12 | 
13 | func WithRegistry(r registry.Registry) Option {
14 | 	return func(o *Options) {
15 | 		o.Registry = r
16 | 	}
17 | }
18 | 


--------------------------------------------------------------------------------
/util/http/roundtripper.go:
--------------------------------------------------------------------------------
 1 | package http
 2 | 
 3 | import (
 4 | 	"errors"
 5 | 	"net/http"
 6 | 
 7 | 	"go-micro.dev/v5/selector"
 8 | )
 9 | 
10 | type roundTripper struct {
11 | 	rt   http.RoundTripper
12 | 	st   selector.Strategy
13 | 	opts Options
14 | }
15 | 
16 | func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
17 | 	s, err := r.opts.Registry.GetService(req.URL.Host)
18 | 	if err != nil {
19 | 		return nil, err
20 | 	}
21 | 
22 | 	next := r.st(s)
23 | 
24 | 	// rudimentary retry 3 times
25 | 	for i := 0; i < 3; i++ {
26 | 		n, err := next()
27 | 		if err != nil {
28 | 			continue
29 | 		}
30 | 		req.URL.Host = n.Address
31 | 		w, err := r.rt.RoundTrip(req)
32 | 		if err != nil {
33 | 			continue
34 | 		}
35 | 		return w, nil
36 | 	}
37 | 
38 | 	return nil, errors.New("failed request")
39 | }
40 | 


--------------------------------------------------------------------------------
/util/jitter/jitter.go:
--------------------------------------------------------------------------------
 1 | // Package jitter provides a random jitter
 2 | package jitter
 3 | 
 4 | import (
 5 | 	"math/rand"
 6 | 	"time"
 7 | )
 8 | 
 9 | var (
10 | 	r = rand.New(rand.NewSource(time.Now().UnixNano()))
11 | )
12 | 
13 | // Do returns a random time to jitter with max cap specified.
14 | func Do(d time.Duration) time.Duration {
15 | 	v := r.Float64() * float64(d.Nanoseconds())
16 | 	return time.Duration(v)
17 | }
18 | 


--------------------------------------------------------------------------------
/util/mdns/.gitignore:
--------------------------------------------------------------------------------
 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
 2 | *.o
 3 | *.a
 4 | *.so
 5 | 
 6 | # Folders
 7 | _obj
 8 | _test
 9 | 
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 | 
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 | 
20 | _testmain.go
21 | 
22 | *.exe
23 | *.test
24 | 


--------------------------------------------------------------------------------
/util/mdns/dns_sd_test.go:
--------------------------------------------------------------------------------
 1 | package mdns
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"github.com/miekg/dns"
 8 | )
 9 | 
10 | type mockMDNSService struct{}
11 | 
12 | func (s *mockMDNSService) Records(q dns.Question) []dns.RR {
13 | 	return []dns.RR{
14 | 		&dns.PTR{
15 | 			Hdr: dns.RR_Header{
16 | 				Name:   "fakerecord",
17 | 				Rrtype: dns.TypePTR,
18 | 				Class:  dns.ClassINET,
19 | 				Ttl:    42,
20 | 			},
21 | 			Ptr: "fake.local.",
22 | 		},
23 | 	}
24 | }
25 | 
26 | func (s *mockMDNSService) Announcement() []dns.RR {
27 | 	return []dns.RR{
28 | 		&dns.PTR{
29 | 			Hdr: dns.RR_Header{
30 | 				Name:   "fakeannounce",
31 | 				Rrtype: dns.TypePTR,
32 | 				Class:  dns.ClassINET,
33 | 				Ttl:    42,
34 | 			},
35 | 			Ptr: "fake.local.",
36 | 		},
37 | 	}
38 | }
39 | 
40 | func TestDNSSDServiceRecords(t *testing.T) {
41 | 	s := &DNSSDService{
42 | 		MDNSService: &MDNSService{
43 | 			serviceAddr: "_foobar._tcp.local.",
44 | 			Domain:      "local",
45 | 		},
46 | 	}
47 | 	q := dns.Question{
48 | 		Name:   "_services._dns-sd._udp.local.",
49 | 		Qtype:  dns.TypePTR,
50 | 		Qclass: dns.ClassINET,
51 | 	}
52 | 	recs := s.Records(q)
53 | 	if got, want := len(recs), 1; got != want {
54 | 		t.Fatalf("s.Records(%v) returned %v records, want %v", q, got, want)
55 | 	}
56 | 
57 | 	want := dns.RR(&dns.PTR{
58 | 		Hdr: dns.RR_Header{
59 | 			Name:   "_services._dns-sd._udp.local.",
60 | 			Rrtype: dns.TypePTR,
61 | 			Class:  dns.ClassINET,
62 | 			Ttl:    defaultTTL,
63 | 		},
64 | 		Ptr: "_foobar._tcp.local.",
65 | 	})
66 | 	if got := recs[0]; !reflect.DeepEqual(got, want) {
67 | 		t.Errorf("s.Records()[0] = %v, want %v", got, want)
68 | 	}
69 | }
70 | 


--------------------------------------------------------------------------------
/util/mdns/server_test.go:
--------------------------------------------------------------------------------
 1 | package mdns
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 	"time"
 6 | )
 7 | 
 8 | func TestServer_StartStop(t *testing.T) {
 9 | 	s := makeService(t)
10 | 	serv, err := NewServer(&Config{Zone: s, LocalhostChecking: true})
11 | 	if err != nil {
12 | 		t.Fatalf("err: %v", err)
13 | 	}
14 | 	defer serv.Shutdown()
15 | }
16 | 
17 | func TestServer_Lookup(t *testing.T) {
18 | 	serv, err := NewServer(&Config{Zone: makeServiceWithServiceName(t, "_foobar._tcp"), LocalhostChecking: true})
19 | 	if err != nil {
20 | 		t.Fatalf("err: %v", err)
21 | 	}
22 | 	defer serv.Shutdown()
23 | 
24 | 	entries := make(chan *ServiceEntry, 1)
25 | 	found := false
26 | 	doneCh := make(chan struct{})
27 | 	go func() {
28 | 		select {
29 | 		case e := <-entries:
30 | 			if e.Name != "hostname._foobar._tcp.local." {
31 | 				t.Fatalf("bad: %v", e)
32 | 			}
33 | 			if e.Port != 80 {
34 | 				t.Fatalf("bad: %v", e)
35 | 			}
36 | 			if e.Info != "Local web server" {
37 | 				t.Fatalf("bad: %v", e)
38 | 			}
39 | 			found = true
40 | 
41 | 		case <-time.After(80 * time.Millisecond):
42 | 			t.Fatalf("timeout")
43 | 		}
44 | 		close(doneCh)
45 | 	}()
46 | 
47 | 	params := &QueryParam{
48 | 		Service: "_foobar._tcp",
49 | 		Domain:  "local",
50 | 		Timeout: 50 * time.Millisecond,
51 | 		Entries: entries,
52 | 	}
53 | 	err = Query(params)
54 | 	if err != nil {
55 | 		t.Fatalf("err: %v", err)
56 | 	}
57 | 	<-doneCh
58 | 	if !found {
59 | 		t.Fatalf("record not found")
60 | 	}
61 | }
62 | 


--------------------------------------------------------------------------------
/util/net/net_test.go:
--------------------------------------------------------------------------------
 1 | package net
 2 | 
 3 | import (
 4 | 	"net"
 5 | 	"os"
 6 | 	"testing"
 7 | )
 8 | 
 9 | func TestListen(t *testing.T) {
10 | 	fn := func(addr string) (net.Listener, error) {
11 | 		return net.Listen("tcp", addr)
12 | 	}
13 | 
14 | 	// try to create a number of listeners
15 | 	for i := 0; i < 10; i++ {
16 | 		l, err := Listen("localhost:10000-11000", fn)
17 | 		if err != nil {
18 | 			t.Fatal(err)
19 | 		}
20 | 		defer l.Close()
21 | 	}
22 | 
23 | 	// TODO nats case test
24 | 	// natsAddr := "_INBOX.bID2CMRvlNp0vt4tgNBHWf"
25 | 	// Expect addr DO NOT has extra ":" at the end!
26 | }
27 | 
28 | // TestProxyEnv checks whether we have proxy/network settings in env.
29 | func TestProxyEnv(t *testing.T) {
30 | 	service := "foo"
31 | 	address := []string{"bar"}
32 | 
33 | 	s, a, ok := Proxy(service, address)
34 | 	if ok {
35 | 		t.Fatal("Should not have proxy", s, a, ok)
36 | 	}
37 | 
38 | 	test := func(key, val, expectSrv, expectAddr string) {
39 | 		// set env
40 | 		os.Setenv(key, val)
41 | 
42 | 		s, a, ok := Proxy(service, address)
43 | 		if !ok {
44 | 			t.Fatal("Expected proxy")
45 | 		}
46 | 		if len(expectSrv) > 0 && s != expectSrv {
47 | 			t.Fatal("Expected proxy service", expectSrv, "got", s)
48 | 		}
49 | 		if len(expectAddr) > 0 {
50 | 			if len(a) == 0 || a[0] != expectAddr {
51 | 				t.Fatal("Expected proxy address", expectAddr, "got", a)
52 | 			}
53 | 		}
54 | 
55 | 		os.Unsetenv(key)
56 | 	}
57 | 
58 | 	test("MICRO_PROXY", "service", "go.micro.proxy", "")
59 | 	test("MICRO_NETWORK", "service", "go.micro.network", "")
60 | 	test("MICRO_NETWORK_ADDRESS", "10.0.0.1:8081", "", "10.0.0.1:8081")
61 | }
62 | 


--------------------------------------------------------------------------------
/util/pool/options.go:
--------------------------------------------------------------------------------
 1 | package pool
 2 | 
 3 | import (
 4 | 	"time"
 5 | 
 6 | 	"go-micro.dev/v5/transport"
 7 | )
 8 | 
 9 | type Options struct {
10 | 	Transport    transport.Transport
11 | 	TTL          time.Duration
12 | 	CloseTimeout time.Duration
13 | 	Size         int
14 | }
15 | 
16 | type Option func(*Options)
17 | 
18 | func Size(i int) Option {
19 | 	return func(o *Options) {
20 | 		o.Size = i
21 | 	}
22 | }
23 | 
24 | func Transport(t transport.Transport) Option {
25 | 	return func(o *Options) {
26 | 		o.Transport = t
27 | 	}
28 | }
29 | 
30 | func TTL(t time.Duration) Option {
31 | 	return func(o *Options) {
32 | 		o.TTL = t
33 | 	}
34 | }
35 | 
36 | func CloseTimeout(t time.Duration) Option {
37 | 	return func(o *Options) {
38 | 		o.CloseTimeout = t
39 | 	}
40 | }
41 | 


--------------------------------------------------------------------------------
/util/pool/pool.go:
--------------------------------------------------------------------------------
 1 | // Package pool is a connection pool
 2 | package pool
 3 | 
 4 | import (
 5 | 	"time"
 6 | 
 7 | 	"go-micro.dev/v5/transport"
 8 | )
 9 | 
10 | // Pool is an interface for connection pooling.
11 | type Pool interface {
12 | 	// Close the pool
13 | 	Close() error
14 | 	// Get a connection
15 | 	Get(addr string, opts ...transport.DialOption) (Conn, error)
16 | 	// Release the connection
17 | 	Release(c Conn, status error) error
18 | }
19 | 
20 | // Conn interface represents a pool connection.
21 | type Conn interface {
22 | 	// unique id of connection
23 | 	Id() string
24 | 	// time it was created
25 | 	Created() time.Time
26 | 	// embedded connection
27 | 	transport.Client
28 | }
29 | 
30 | // NewPool will return a new pool object.
31 | func NewPool(opts ...Option) Pool {
32 | 	var options Options
33 | 	for _, o := range opts {
34 | 		o(&options)
35 | 	}
36 | 
37 | 	return newPool(options)
38 | }
39 | 


--------------------------------------------------------------------------------
/util/registry/util_test.go:
--------------------------------------------------------------------------------
 1 | package registry
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"testing"
 6 | 
 7 | 	"go-micro.dev/v5/registry"
 8 | )
 9 | 
10 | func TestRemove(t *testing.T) {
11 | 	services := []*registry.Service{
12 | 		{
13 | 			Name:    "foo",
14 | 			Version: "1.0.0",
15 | 			Nodes: []*registry.Node{
16 | 				{
17 | 					Id:      "foo-123",
18 | 					Address: "localhost:9999",
19 | 				},
20 | 			},
21 | 		},
22 | 		{
23 | 			Name:    "foo",
24 | 			Version: "1.0.0",
25 | 			Nodes: []*registry.Node{
26 | 				{
27 | 					Id:      "foo-123",
28 | 					Address: "localhost:6666",
29 | 				},
30 | 			},
31 | 		},
32 | 	}
33 | 
34 | 	servs := Remove([]*registry.Service{services[0]}, []*registry.Service{services[1]})
35 | 	if i := len(servs); i > 0 {
36 | 		t.Errorf("Expected 0 nodes, got %d: %+v", i, servs)
37 | 	}
38 | 	if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
39 | 		t.Logf("Services %+v", servs)
40 | 	}
41 | }
42 | 
43 | func TestRemoveNodes(t *testing.T) {
44 | 	services := []*registry.Service{
45 | 		{
46 | 			Name:    "foo",
47 | 			Version: "1.0.0",
48 | 			Nodes: []*registry.Node{
49 | 				{
50 | 					Id:      "foo-123",
51 | 					Address: "localhost:9999",
52 | 				},
53 | 				{
54 | 					Id:      "foo-321",
55 | 					Address: "localhost:6666",
56 | 				},
57 | 			},
58 | 		},
59 | 		{
60 | 			Name:    "foo",
61 | 			Version: "1.0.0",
62 | 			Nodes: []*registry.Node{
63 | 				{
64 | 					Id:      "foo-123",
65 | 					Address: "localhost:6666",
66 | 				},
67 | 			},
68 | 		},
69 | 	}
70 | 
71 | 	nodes := delNodes(services[0].Nodes, services[1].Nodes)
72 | 	if i := len(nodes); i != 1 {
73 | 		t.Errorf("Expected only 1 node, got %d: %+v", i, nodes)
74 | 	}
75 | 	if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
76 | 		t.Logf("Nodes %+v", nodes)
77 | 	}
78 | }
79 | 


--------------------------------------------------------------------------------
/util/ring/buffer_test.go:
--------------------------------------------------------------------------------
 1 | package ring
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 	"time"
 6 | )
 7 | 
 8 | func TestBuffer(t *testing.T) {
 9 | 	b := New(10)
10 | 
11 | 	// test one value
12 | 	b.Put("foo")
13 | 	v := b.Get(1)
14 | 
15 | 	if val := v[0].Value.(string); val != "foo" {
16 | 		t.Fatalf("expected foo got %v", val)
17 | 	}
18 | 
19 | 	b = New(10)
20 | 
21 | 	// test 10 values
22 | 	for i := 0; i < 10; i++ {
23 | 		b.Put(i)
24 | 	}
25 | 
26 | 	d := time.Now()
27 | 	v = b.Get(10)
28 | 
29 | 	for i := 0; i < 10; i++ {
30 | 		val := v[i].Value.(int)
31 | 
32 | 		if val != i {
33 | 			t.Fatalf("expected %d got %d", i, val)
34 | 		}
35 | 	}
36 | 
37 | 	// test more values
38 | 
39 | 	for i := 0; i < 10; i++ {
40 | 		v := i * 2
41 | 		b.Put(v)
42 | 	}
43 | 
44 | 	v = b.Get(10)
45 | 
46 | 	for i := 0; i < 10; i++ {
47 | 		val := v[i].Value.(int)
48 | 		expect := i * 2
49 | 		if val != expect {
50 | 			t.Fatalf("expected %d got %d", expect, val)
51 | 		}
52 | 	}
53 | 
54 | 	// sleep 100 ms
55 | 	time.Sleep(time.Millisecond * 100)
56 | 
57 | 	// assume we'll get everything
58 | 	v = b.Since(d)
59 | 
60 | 	if len(v) != 10 {
61 | 		t.Fatalf("expected 10 entries but got %d", len(v))
62 | 	}
63 | 
64 | 	// write 1 more entry
65 | 	d = time.Now()
66 | 	b.Put(100)
67 | 
68 | 	// sleep 100 ms
69 | 	time.Sleep(time.Millisecond * 100)
70 | 
71 | 	v = b.Since(d)
72 | 	if len(v) != 1 {
73 | 		t.Fatalf("expected 1 entries but got %d", len(v))
74 | 	}
75 | 
76 | 	if v[0].Value.(int) != 100 {
77 | 		t.Fatalf("expected value 100 got %v", v[0])
78 | 	}
79 | }
80 | 


--------------------------------------------------------------------------------
/util/signal/signal.go:
--------------------------------------------------------------------------------
 1 | package signal
 2 | 
 3 | import (
 4 | 	"os"
 5 | 	"syscall"
 6 | )
 7 | 
 8 | // ShutDownSingals returns all the signals that are being watched for to shut down services.
 9 | func Shutdown() []os.Signal {
10 | 	return []os.Signal{
11 | 		syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL,
12 | 	}
13 | }
14 | 


--------------------------------------------------------------------------------
/util/socket/pool.go:
--------------------------------------------------------------------------------
 1 | package socket
 2 | 
 3 | import (
 4 | 	"sync"
 5 | )
 6 | 
 7 | type Pool struct {
 8 | 	pool map[string]*Socket
 9 | 	sync.RWMutex
10 | }
11 | 
12 | func (p *Pool) Get(id string) (*Socket, bool) {
13 | 	// attempt to get existing socket
14 | 	p.RLock()
15 | 	socket, ok := p.pool[id]
16 | 	if ok {
17 | 		p.RUnlock()
18 | 		return socket, ok
19 | 	}
20 | 	p.RUnlock()
21 | 
22 | 	// save socket
23 | 	p.Lock()
24 | 	defer p.Unlock()
25 | 	// double checked locking
26 | 	socket, ok = p.pool[id]
27 | 	if ok {
28 | 		return socket, ok
29 | 	}
30 | 	// create new socket
31 | 	socket = New(id)
32 | 	p.pool[id] = socket
33 | 
34 | 	// return socket
35 | 	return socket, false
36 | }
37 | 
38 | func (p *Pool) Release(s *Socket) {
39 | 	p.Lock()
40 | 	defer p.Unlock()
41 | 
42 | 	// close the socket
43 | 	s.Close()
44 | 	delete(p.pool, s.id)
45 | }
46 | 
47 | // Close the pool and delete all the sockets.
48 | func (p *Pool) Close() {
49 | 	p.Lock()
50 | 	defer p.Unlock()
51 | 	for id, sock := range p.pool {
52 | 		sock.Close()
53 | 		delete(p.pool, id)
54 | 	}
55 | }
56 | 
57 | // NewPool returns a new socket pool.
58 | func NewPool() *Pool {
59 | 	return &Pool{
60 | 		pool: make(map[string]*Socket),
61 | 	}
62 | }
63 | 


--------------------------------------------------------------------------------
/util/test/test.go:
--------------------------------------------------------------------------------
 1 | package test
 2 | 
 3 | import (
 4 | 	"go-micro.dev/v5/registry"
 5 | )
 6 | 
 7 | var (
 8 | 	// Data is a set of mock registry data.
 9 | 	Data = map[string][]*registry.Service{
10 | 		"foo": {
11 | 			{
12 | 				Name:    "foo",
13 | 				Version: "1.0.0",
14 | 				Nodes: []*registry.Node{
15 | 					{
16 | 						Id:      "foo-1.0.0-123",
17 | 						Address: "localhost:9999",
18 | 					},
19 | 					{
20 | 						Id:      "foo-1.0.0-321",
21 | 						Address: "localhost:9999",
22 | 					},
23 | 				},
24 | 			},
25 | 			{
26 | 				Name:    "foo",
27 | 				Version: "1.0.1",
28 | 				Nodes: []*registry.Node{
29 | 					{
30 | 						Id:      "foo-1.0.1-321",
31 | 						Address: "localhost:6666",
32 | 					},
33 | 				},
34 | 			},
35 | 			{
36 | 				Name:    "foo",
37 | 				Version: "1.0.3",
38 | 				Nodes: []*registry.Node{
39 | 					{
40 | 						Id:      "foo-1.0.3-345",
41 | 						Address: "localhost:8888",
42 | 					},
43 | 				},
44 | 			},
45 | 		},
46 | 	}
47 | )
48 | 
49 | // EmptyChannel will empty out a error channel by checking if an error is
50 | // present, and if so return the error.
51 | func EmptyChannel(c chan error) error {
52 | 	select {
53 | 	case err := <-c:
54 | 		return err
55 | 	default:
56 | 		return nil
57 | 	}
58 | }
59 | 


--------------------------------------------------------------------------------
/web/web.go:
--------------------------------------------------------------------------------
 1 | // Package web provides web based micro services
 2 | package web
 3 | 
 4 | import (
 5 | 	"context"
 6 | 	"net/http"
 7 | 	"time"
 8 | 
 9 | 	"github.com/google/uuid"
10 | )
11 | 
12 | // Service is a web service with service discovery built in.
13 | type Service interface {
14 | 	Client() *http.Client
15 | 	Init(opts ...Option) error
16 | 	Options() Options
17 | 	Handle(pattern string, handler http.Handler)
18 | 	HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
19 | 	Start() error
20 | 	Stop() error
21 | 	Run() error
22 | }
23 | 
24 | // Option for web.
25 | type Option func(o *Options)
26 | 
27 | // Web basic Defaults.
28 | var (
29 | 	// For serving.
30 | 	DefaultName    = "go-web"
31 | 	DefaultVersion = "latest"
32 | 	DefaultId      = uuid.New().String()
33 | 	DefaultAddress = ":0"
34 | 
35 | 	// for registration.
36 | 	DefaultRegisterTTL      = time.Second * 90
37 | 	DefaultRegisterInterval = time.Second * 30
38 | 
39 | 	// static directory.
40 | 	DefaultStaticDir     = "html"
41 | 	DefaultRegisterCheck = func(context.Context) error { return nil }
42 | )
43 | 
44 | // NewService returns a new web.Service.
45 | func NewService(opts ...Option) Service {
46 | 	return newService(opts...)
47 | }
48 | 


--------------------------------------------------------------------------------
/web/web_test.go:
--------------------------------------------------------------------------------
 1 | package web_test
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"sync"
 6 | 	"testing"
 7 | 	"time"
 8 | 
 9 | 	"github.com/urfave/cli/v2"
10 | 	"go-micro.dev/v5"
11 | 	"go-micro.dev/v5/logger"
12 | 	"go-micro.dev/v5/web"
13 | )
14 | 
15 | func TestWeb(t *testing.T) {
16 | 	for i := 0; i < 10; i++ {
17 | 		testFunc()
18 | 	}
19 | }
20 | 
21 | func testFunc() {
22 | 	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*250)
23 | 	defer cancel()
24 | 
25 | 	service := micro.NewService(
26 | 		micro.Name("test"),
27 | 		micro.Context(ctx),
28 | 		micro.HandleSignal(false),
29 | 		micro.Flags(
30 | 			&cli.StringFlag{
31 | 				Name: "test.timeout",
32 | 			},
33 | 			&cli.BoolFlag{
34 | 				Name: "test.v",
35 | 			},
36 | 			&cli.StringFlag{
37 | 				Name: "test.run",
38 | 			},
39 | 			&cli.StringFlag{
40 | 				Name: "test.testlogfile",
41 | 			},
42 | 		),
43 | 	)
44 | 	w := web.NewService(
45 | 		web.MicroService(service),
46 | 		web.Context(ctx),
47 | 		web.HandleSignal(false),
48 | 	)
49 | 	// s.Init()
50 | 	// w.Init()
51 | 
52 | 	var wg sync.WaitGroup
53 | 	wg.Add(2)
54 | 	go func() {
55 | 		defer wg.Done()
56 | 		err := service.Run()
57 | 		if err != nil {
58 | 			logger.Logf(logger.ErrorLevel, "micro run error: %v", err)
59 | 		}
60 | 	}()
61 | 	go func() {
62 | 		defer wg.Done()
63 | 		err := w.Run()
64 | 		if err != nil {
65 | 			logger.Logf(logger.ErrorLevel, "web run error: %v", err)
66 | 		}
67 | 	}()
68 | 
69 | 	wg.Wait()
70 | }
71 | 


--------------------------------------------------------------------------------
/wrapper/trace/opentelemetry/README.md:
--------------------------------------------------------------------------------
 1 | # OpenTelemetry wrappers
 2 | 
 3 | OpenTelemetry wrappers propagate traces (spans) accross services.
 4 | 
 5 | ## Usage
 6 | 
 7 | ```go
 8 | service := micro.NewService(
 9 |     micro.Name("go.micro.srv.greeter"),
10 |     micro.WrapClient(opentelemetry.NewClientWrapper()),
11 |     micro.WrapHandler(opentelemetry.NewHandlerWrapper()),
12 |     micro.WrapSubscriber(opentelemetry.NewSubscriberWrapper()),
13 | )
14 | ```


--------------------------------------------------------------------------------