New upstream snapshot.
Debian Janitor
5 months ago
0 | examples/addsvc/addsvc | |
1 | examples/addsvc/client/client | |
2 | examples/apigateway/apigateway | |
3 | examples/profilesvc/profilesvc | |
4 | examples/stringsvc1/stringsvc1 | |
5 | examples/stringsvc2/stringsvc2 | |
6 | examples/stringsvc3/stringsvc3 | |
7 | *.coverprofile | |
8 | ||
9 | # Compiled Object files, Static and Dynamic libs (Shared Objects) | |
10 | *.o | |
11 | *.a | |
12 | *.so | |
13 | ||
14 | # Folders | |
15 | _obj | |
16 | _test | |
17 | _old* | |
18 | ||
19 | # Architecture specific extensions/prefixes | |
20 | *.[568vq] | |
21 | [568vq].out | |
22 | ||
23 | *.cgo1.go | |
24 | *.cgo2.c | |
25 | _cgo_defun.c | |
26 | _cgo_gotypes.go | |
27 | _cgo_export.* | |
28 | ||
29 | _testmain.go | |
30 | ||
31 | *.exe | |
32 | ||
33 | # https://github.com/github/gitignore/blob/master/Global/Vim.gitignore | |
34 | # swap | |
35 | [._]*.s[a-w][a-z] | |
36 | [._]s[a-w][a-z] | |
37 | # session | |
38 | Session.vim | |
39 | # temporary | |
40 | .netrwhist | |
41 | *~ | |
42 | # auto-generated tag files | |
43 | tags | |
44 |
0 | language: go | |
1 | ||
2 | env: | |
3 | - COVERALLS_TOKEN=MYSkSqcsWXd6DmP6TnSeiDhtvuL4u6ndp | |
4 | ||
5 | before_install: | |
6 | - go get github.com/mattn/goveralls | |
7 | - go get github.com/modocache/gover | |
8 | ||
9 | script: | |
10 | - go test -race -v ./... | |
11 | - ./coveralls.bash | |
12 | ||
13 | go: | |
14 | - 1.9.x | |
15 | - tip |
0 | # Go kit [](https://circleci.com/gh/go-kit/kit) [](https://travis-ci.org/go-kit/kit) [](https://godoc.org/github.com/go-kit/kit) [](https://coveralls.io/github/go-kit/kit?branch=master) [](https://goreportcard.com/report/go-kit/kit) [](https://sourcegraph.com/github.com/go-kit/kit?badge) | |
0 | # Go kit | |
1 | 1 | |
2 | **Go kit** is a **programming toolkit** for building microservices | |
3 | (or elegant monoliths) in Go. We solve common problems in distributed | |
2 |  | |
3 | [](https://pkg.go.dev/github.com/go-kit/kit?tab=doc) | |
4 | [](https://codecov.io/gh/go-kit/kit) | |
5 | [](https://goreportcard.com/report/go-kit/kit) | |
6 | [](https://sourcegraph.com/github.com/go-kit/kit?badge) | |
7 | ||
8 | **Go kit** is a **programming toolkit** for building microservices | |
9 | (or elegant monoliths) in Go. We solve common problems in distributed | |
4 | 10 | systems and application architecture so you can focus on delivering |
5 | 11 | business value. |
6 | 12 | |
7 | 13 | - Website: [gokit.io](https://gokit.io) |
8 | 14 | - Mailing list: [go-kit](https://groups.google.com/forum/#!forum/go-kit) |
9 | 15 | - Slack: [gophers.slack.com](https://gophers.slack.com) **#go-kit** ([invite](https://gophersinvite.herokuapp.com/)) |
16 | ||
17 | ## Sponsors | |
18 | ||
19 | Click [here](https://github.com/sponsors/peterbourgon) or Sponsor, above, for more information on sponsorship. | |
10 | 20 | |
11 | 21 | ## Motivation |
12 | 22 | |
49 | 59 | |
50 | 60 | ## Dependency management |
51 | 61 | |
52 | Go kit is a library, designed to be imported into a binary package. Vendoring | |
53 | is currently the best way for binary package authors to ensure reliable, | |
54 | reproducible builds. Therefore, we strongly recommend our users use vendoring | |
55 | for all of their dependencies, including Go kit. To avoid compatibility and | |
56 | availability issues, Go kit doesn't vendor its own dependencies, and | |
57 | doesn't recommend use of third-party import proxies. | |
62 | Go kit is [modules](https://github.com/golang/go/wiki/Modules) aware, and we | |
63 | encourage users to use the standard modules tooling. But Go kit is at major | |
64 | version 0, so it should be compatible with non-modules environments. | |
58 | 65 | |
59 | There are several tools which make vendoring easier, including | |
60 | [dep](https://github.com/golang/dep), | |
61 | [gb](http://getgb.io), | |
62 | [glide](https://github.com/Masterminds/glide), | |
63 | [gvt](https://github.com/FiloSottile/gvt), and | |
64 | [govendor](https://github.com/kardianos/govendor). | |
65 | In addition, Go kit uses a variety of continuous integration providers | |
66 | to find and fix compatibility problems as soon as they occur. | |
66 | ## Code generators | |
67 | ||
68 | There are several third-party tools that can generate Go kit code based on | |
69 | different starting assumptions. | |
70 | ||
71 | - [devimteam/microgen](https://github.com/devimteam/microgen) | |
72 | - [GrantZheng/kit](https://github.com/GrantZheng/kit) | |
73 | - [chaseSpace/kit](https://github.com/chaseSpace/kit) | |
74 | - [kujtimiihoxha/kit](https://github.com/kujtimiihoxha/kit) (unmaintained) | |
75 | - [nytimes/marvin](https://github.com/nytimes/marvin) | |
76 | - [sagikazarmark/mga](https://github.com/sagikazarmark/mga) | |
77 | - [sagikazarmark/protoc-gen-go-kit](https://github.com/sagikazarmark/protoc-gen-go-kit) | |
78 | - [tuneinc/truss](https://github.com/tuneinc/truss) | |
67 | 79 | |
68 | 80 | ## Related projects |
69 | 81 | |
72 | 84 | ### Service frameworks |
73 | 85 | |
74 | 86 | - [gizmo](https://github.com/nytimes/gizmo), a microservice toolkit from The New York Times ★ |
75 | - [go-micro](https://github.com/myodc/go-micro), a microservices client/server library ★ | |
87 | - [go-micro](https://github.com/micro/go-micro), a distributed systems development framework ★ | |
76 | 88 | - [gotalk](https://github.com/rsms/gotalk), async peer communication protocol & library |
77 | 89 | - [Kite](https://github.com/koding/kite), a micro-service framework |
78 | 90 | - [gocircuit](https://github.com/gocircuit/circuit), dynamic cloud orchestration |
90 | 102 | - [mattheath/phosphor](https://github.com/mondough/phosphor), distributed system tracing |
91 | 103 | - [pivotal-golang/lager](https://github.com/pivotal-golang/lager), an opinionated logging library |
92 | 104 | - [rubyist/circuitbreaker](https://github.com/rubyist/circuitbreaker), circuit breaker library |
93 | - [Sirupsen/logrus](https://github.com/Sirupsen/logrus), structured, pluggable logging for Go ★ | |
105 | - [sirupsen/logrus](https://github.com/sirupsen/logrus), structured, pluggable logging for Go ★ | |
94 | 106 | - [sourcegraph/appdash](https://github.com/sourcegraph/appdash), application tracing system based on Google's Dapper |
95 | 107 | - [spacemonkeygo/monitor](https://github.com/spacemonkeygo/monitor), data collection, monitoring, instrumentation, and Zipkin client library |
96 | 108 | - [streadway/handy](https://github.com/streadway/handy), net/http handler filters |
100 | 112 | ### Web frameworks |
101 | 113 | |
102 | 114 | - [Gorilla](http://www.gorillatoolkit.org) |
103 | - [Gin](https://gin-gonic.github.io/gin/) | |
115 | - [Gin](https://gin-gonic.com/) | |
104 | 116 | - [Negroni](https://github.com/codegangsta/negroni) |
105 | 117 | - [Goji](https://github.com/zenazn/goji) |
106 | 118 | - [Martini](https://github.com/go-martini/martini) |
107 | - [Beego](http://beego.me/) | |
119 | - [Beego](https://beego.vip/) | |
108 | 120 | - [Revel](https://revel.github.io/) (considered [harmful](https://github.com/go-kit/kit/issues/350)) |
121 | - [GoBuffalo](https://gobuffalo.io/) | |
109 | 122 | |
110 | 123 | ## Additional reading |
111 | 124 | |
112 | - [Architecting for the Cloud](http://fr.slideshare.net/stonse/architecting-for-the-cloud-using-netflixoss-codemash-workshop-29852233) — Netflix | |
125 | - [Architecting for the Cloud](https://slideshare.net/stonse/architecting-for-the-cloud-using-netflixoss-codemash-workshop-29852233) — Netflix | |
113 | 126 | - [Dapper, a Large-Scale Distributed Systems Tracing Infrastructure](http://research.google.com/pubs/pub36356.html) — Google |
114 | 127 | - [Your Server as a Function](http://monkey.org/~marius/funsrv.pdf) (PDF) — Twitter |
115 | ||
116 | --- | |
117 | ||
118 | Development supported by [DigitalOcean](https://digitalocean.com). |
0 | # Roadmap | |
1 | ||
2 | This is a coarse-grained roadmap of Go kit development direction in the short | |
3 | to mid-term future. It will be kept reasonably up-to-date by the project | |
4 | maintainers. Suggest new ideas, enhancements, and features using the standard | |
5 | [issues](https://github.com/go-kit/kit/issues) model. | |
6 | ||
7 | ## Prioritized | |
8 | ||
9 | 1. kitgen code generation (#308, #70) | |
10 | 1. package pubsub (#298, #295) | |
11 | ||
12 | ## Unprioritized | |
13 | ||
14 | - package log/levels refactor (#250, #269, #252) | |
15 | - package auth/jwt (#255) | |
16 |
0 | package casbin | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | ||
6 | stdcasbin "github.com/casbin/casbin/v2" | |
7 | "github.com/go-kit/kit/endpoint" | |
8 | ) | |
9 | ||
10 | type contextKey string | |
11 | ||
12 | const ( | |
13 | // CasbinModelContextKey holds the key to store the access control model | |
14 | // in context, it can be a path to configuration file or a casbin/model | |
15 | // Model. | |
16 | CasbinModelContextKey contextKey = "CasbinModel" | |
17 | ||
18 | // CasbinPolicyContextKey holds the key to store the access control policy | |
19 | // in context, it can be a path to policy file or an implementation of | |
20 | // casbin/persist Adapter interface. | |
21 | CasbinPolicyContextKey contextKey = "CasbinPolicy" | |
22 | ||
23 | // CasbinEnforcerContextKey holds the key to retrieve the active casbin | |
24 | // Enforcer. | |
25 | CasbinEnforcerContextKey contextKey = "CasbinEnforcer" | |
26 | ) | |
27 | ||
28 | var ( | |
29 | // ErrModelContextMissing denotes a casbin model was not passed into | |
30 | // the parsing of middleware's context. | |
31 | ErrModelContextMissing = errors.New("CasbinModel is required in context") | |
32 | ||
33 | // ErrPolicyContextMissing denotes a casbin policy was not passed into | |
34 | // the parsing of middleware's context. | |
35 | ErrPolicyContextMissing = errors.New("CasbinPolicy is required in context") | |
36 | ||
37 | // ErrUnauthorized denotes the subject is not authorized to do the action | |
38 | // intended on the given object, based on the context model and policy. | |
39 | ErrUnauthorized = errors.New("Unauthorized Access") | |
40 | ) | |
41 | ||
42 | // NewEnforcer checks whether the subject is authorized to do the specified | |
43 | // action on the given object. If a valid access control model and policy | |
44 | // is given, then the generated casbin Enforcer is stored in the context | |
45 | // with CasbinEnforcer as the key. | |
46 | func NewEnforcer( | |
47 | subject string, object interface{}, action string, | |
48 | ) endpoint.Middleware { | |
49 | return func(next endpoint.Endpoint) endpoint.Endpoint { | |
50 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { | |
51 | casbinModel := ctx.Value(CasbinModelContextKey) | |
52 | casbinPolicy := ctx.Value(CasbinPolicyContextKey) | |
53 | enforcer, err := stdcasbin.NewEnforcer(casbinModel, casbinPolicy) | |
54 | if err != nil { | |
55 | return nil, err | |
56 | } | |
57 | ||
58 | ctx = context.WithValue(ctx, CasbinEnforcerContextKey, enforcer) | |
59 | ok, err := enforcer.Enforce(subject, object, action) | |
60 | if err != nil { | |
61 | return nil, err | |
62 | } | |
63 | if !ok { | |
64 | return nil, ErrUnauthorized | |
65 | } | |
66 | ||
67 | return next(ctx, request) | |
68 | } | |
69 | } | |
70 | } |
0 | package casbin | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "testing" | |
5 | ||
6 | stdcasbin "github.com/casbin/casbin/v2" | |
7 | "github.com/casbin/casbin/v2/model" | |
8 | fileadapter "github.com/casbin/casbin/v2/persist/file-adapter" | |
9 | ) | |
10 | ||
11 | func TestStructBaseContext(t *testing.T) { | |
12 | e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil } | |
13 | ||
14 | m := model.NewModel() | |
15 | m.AddDef("r", "r", "sub, obj, act") | |
16 | m.AddDef("p", "p", "sub, obj, act") | |
17 | m.AddDef("e", "e", "some(where (p.eft == allow))") | |
18 | m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)") | |
19 | ||
20 | a := fileadapter.NewAdapter("testdata/keymatch_policy.csv") | |
21 | ||
22 | ctx := context.WithValue(context.Background(), CasbinModelContextKey, m) | |
23 | ctx = context.WithValue(ctx, CasbinPolicyContextKey, a) | |
24 | ||
25 | // positive case | |
26 | middleware := NewEnforcer("alice", "/alice_data/resource1", "GET")(e) | |
27 | ctx1, err := middleware(ctx, struct{}{}) | |
28 | if err != nil { | |
29 | t.Fatalf("Enforcer returned error: %s", err) | |
30 | } | |
31 | _, ok := ctx1.(context.Context).Value(CasbinEnforcerContextKey).(*stdcasbin.Enforcer) | |
32 | if !ok { | |
33 | t.Fatalf("context should contains the active enforcer") | |
34 | } | |
35 | ||
36 | // negative case | |
37 | middleware = NewEnforcer("alice", "/alice_data/resource2", "POST")(e) | |
38 | _, err = middleware(ctx, struct{}{}) | |
39 | if err == nil { | |
40 | t.Fatalf("Enforcer should return error") | |
41 | } | |
42 | } | |
43 | ||
44 | func TestFileBaseContext(t *testing.T) { | |
45 | e := func(ctx context.Context, i interface{}) (interface{}, error) { return ctx, nil } | |
46 | ctx := context.WithValue(context.Background(), CasbinModelContextKey, "testdata/basic_model.conf") | |
47 | ctx = context.WithValue(ctx, CasbinPolicyContextKey, "testdata/basic_policy.csv") | |
48 | ||
49 | // positive case | |
50 | middleware := NewEnforcer("alice", "data1", "read")(e) | |
51 | _, err := middleware(ctx, struct{}{}) | |
52 | if err != nil { | |
53 | t.Fatalf("Enforcer returned error: %s", err) | |
54 | } | |
55 | } |
0 | [request_definition] | |
1 | r = sub, obj, act | |
2 | ||
3 | [policy_definition] | |
4 | p = sub, obj, act | |
5 | ||
6 | [policy_effect] | |
7 | e = some(where (p.eft == allow)) | |
8 | ||
9 | [matchers] | |
10 | m = r.sub == p.sub && r.obj == p.obj && r.act == p.act⏎ |
0 | p, alice, /alice_data/*, GET | |
1 | p, alice, /alice_data/resource1, POST | |
2 | ||
3 | p, bob, /alice_data/resource2, GET | |
4 | p, bob, /bob_data/*, POST | |
5 | ||
6 | p, cathy, /cathy_data, (GET)|(POST)⏎ |
6 | 6 | |
7 | 7 | NewParser takes a key function and an expected signing method and returns an |
8 | 8 | `endpoint.Middleware`. The middleware will parse a token passed into the |
9 | context via the `jwt.JWTTokenContextKey`. If the token is valid, any claims | |
9 | context via the `jwt.JWTContextKey`. If the token is valid, any claims | |
10 | 10 | will be added to the context via the `jwt.JWTClaimsContextKey`. |
11 | 11 | |
12 | 12 | ```go |
13 | 13 | import ( |
14 | stdjwt "github.com/dgrijalva/jwt-go" | |
14 | stdjwt "github.com/golang-jwt/jwt/v4" | |
15 | 15 | |
16 | 16 | "github.com/go-kit/kit/auth/jwt" |
17 | 17 | "github.com/go-kit/kit/endpoint" |
29 | 29 | |
30 | 30 | NewSigner takes a JWT key ID header, the signing key, signing method, and a |
31 | 31 | claims object. It returns an `endpoint.Middleware`. The middleware will build |
32 | the token string and add it to the context via the `jwt.JWTTokenContextKey`. | |
32 | the token string and add it to the context via the `jwt.JWTContextKey`. | |
33 | 33 | |
34 | 34 | ```go |
35 | 35 | import ( |
36 | stdjwt "github.com/dgrijalva/jwt-go" | |
36 | stdjwt "github.com/golang-jwt/jwt/v4" | |
37 | 37 | |
38 | 38 | "github.com/go-kit/kit/auth/jwt" |
39 | 39 | "github.com/go-kit/kit/endpoint" |
54 | 54 | ``` |
55 | 55 | |
56 | 56 | In order for the parser and the signer to work, the authorization headers need |
57 | to be passed between the request and the context. `ToHTTPContext()`, | |
58 | `FromHTTPContext()`, `ToGRPCContext()`, and `FromGRPCContext()` are given as | |
57 | to be passed between the request and the context. `HTTPToContext()`, | |
58 | `ContextToHTTP()`, `GRPCToContext()`, and `ContextToGRPC()` are given as | |
59 | 59 | helpers to do this. These functions implement the correlating transport's |
60 | 60 | RequestFunc interface and can be passed as ClientBefore or ServerBefore |
61 | 61 | options. |
64 | 64 | |
65 | 65 | ```go |
66 | 66 | import ( |
67 | stdjwt "github.com/dgrijalva/jwt-go" | |
67 | stdjwt "github.com/golang-jwt/jwt/v4" | |
68 | 68 | |
69 | 69 | grpctransport "github.com/go-kit/kit/transport/grpc" |
70 | 70 | "github.com/go-kit/kit/auth/jwt" |
76 | 76 | options := []httptransport.ClientOption{} |
77 | 77 | var exampleEndpoint endpoint.Endpoint |
78 | 78 | { |
79 | exampleEndpoint = grpctransport.NewClient(..., grpctransport.ClientBefore(jwt.FromGRPCContext())).Endpoint() | |
79 | exampleEndpoint = grpctransport.NewClient(..., grpctransport.ClientBefore(jwt.ContextToGRPC())).Endpoint() | |
80 | 80 | exampleEndpoint = jwt.NewSigner( |
81 | 81 | "kid-header", |
82 | 82 | []byte("SigningString"), |
94 | 94 | "context" |
95 | 95 | |
96 | 96 | "github.com/go-kit/kit/auth/jwt" |
97 | "github.com/go-kit/kit/log" | |
97 | "github.com/go-kit/log" | |
98 | 98 | grpctransport "github.com/go-kit/kit/transport/grpc" |
99 | 99 | ) |
100 | 100 | |
107 | 107 | endpoints.CreateUserEndpoint, |
108 | 108 | DecodeGRPCCreateUserRequest, |
109 | 109 | EncodeGRPCCreateUserResponse, |
110 | append(options, grpctransport.ServerBefore(jwt.ToGRPCContext()))..., | |
110 | append(options, grpctransport.ServerBefore(jwt.GRPCToContext()))..., | |
111 | 111 | ), |
112 | 112 | getUser: grpctransport.NewServer( |
113 | 113 | ctx, |
3 | 3 | "context" |
4 | 4 | "errors" |
5 | 5 | |
6 | jwt "github.com/dgrijalva/jwt-go" | |
7 | ||
8 | 6 | "github.com/go-kit/kit/endpoint" |
7 | "github.com/golang-jwt/jwt/v4" | |
9 | 8 | ) |
10 | 9 | |
11 | 10 | type contextKey string |
12 | 11 | |
13 | 12 | const ( |
14 | // JWTTokenContextKey holds the key used to store a JWT Token in the | |
15 | // context. | |
16 | JWTTokenContextKey contextKey = "JWTToken" | |
13 | // JWTContextKey holds the key used to store a JWT in the context. | |
14 | JWTContextKey contextKey = "JWTToken" | |
15 | ||
16 | // JWTTokenContextKey is an alias for JWTContextKey. | |
17 | // | |
18 | // Deprecated: prefer JWTContextKey. | |
19 | JWTTokenContextKey = JWTContextKey | |
17 | 20 | |
18 | 21 | // JWTClaimsContextKey holds the key used to store the JWT Claims in the |
19 | 22 | // context. |
26 | 29 | ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context") |
27 | 30 | |
28 | 31 | // ErrTokenInvalid denotes a token was not able to be validated. |
29 | ErrTokenInvalid = errors.New("JWT Token was invalid") | |
32 | ErrTokenInvalid = errors.New("JWT was invalid") | |
30 | 33 | |
31 | 34 | // ErrTokenExpired denotes a token's expire header (exp) has since passed. |
32 | ErrTokenExpired = errors.New("JWT Token is expired") | |
35 | ErrTokenExpired = errors.New("JWT is expired") | |
33 | 36 | |
34 | // ErrTokenMalformed denotes a token was not formatted as a JWT token. | |
35 | ErrTokenMalformed = errors.New("JWT Token is malformed") | |
37 | // ErrTokenMalformed denotes a token was not formatted as a JWT. | |
38 | ErrTokenMalformed = errors.New("JWT is malformed") | |
36 | 39 | |
37 | 40 | // ErrTokenNotActive denotes a token's not before header (nbf) is in the |
38 | 41 | // future. |
43 | 46 | ErrUnexpectedSigningMethod = errors.New("unexpected signing method") |
44 | 47 | ) |
45 | 48 | |
46 | // NewSigner creates a new JWT token generating middleware, specifying key ID, | |
49 | // NewSigner creates a new JWT generating middleware, specifying key ID, | |
47 | 50 | // signing string, signing method and the claims you would like it to contain. |
48 | 51 | // Tokens are signed with a Key ID header (kid) which is useful for determining |
49 | 52 | // the key to use for parsing. Particularly useful for clients. |
58 | 61 | if err != nil { |
59 | 62 | return nil, err |
60 | 63 | } |
61 | ctx = context.WithValue(ctx, JWTTokenContextKey, tokenString) | |
64 | ctx = context.WithValue(ctx, JWTContextKey, tokenString) | |
62 | 65 | |
63 | 66 | return next(ctx, request) |
64 | 67 | } |
81 | 84 | return &jwt.StandardClaims{} |
82 | 85 | } |
83 | 86 | |
84 | // NewParser creates a new JWT token parsing middleware, specifying a | |
87 | // NewParser creates a new JWT parsing middleware, specifying a | |
85 | 88 | // jwt.Keyfunc interface, the signing method and the claims type to be used. NewParser |
86 | 89 | // adds the resulting claims to endpoint context or returns error on invalid token. |
87 | 90 | // Particularly useful for servers. |
89 | 92 | return func(next endpoint.Endpoint) endpoint.Endpoint { |
90 | 93 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { |
91 | 94 | // tokenString is stored in the context from the transport handlers. |
92 | tokenString, ok := ctx.Value(JWTTokenContextKey).(string) | |
95 | tokenString, ok := ctx.Value(JWTContextKey).(string) | |
93 | 96 | if !ok { |
94 | 97 | return nil, ErrTokenContextMissing |
95 | 98 | } |
7 | 7 | |
8 | 8 | "crypto/subtle" |
9 | 9 | |
10 | jwt "github.com/dgrijalva/jwt-go" | |
11 | 10 | "github.com/go-kit/kit/endpoint" |
11 | "github.com/golang-jwt/jwt/v4" | |
12 | 12 | ) |
13 | 13 | |
14 | 14 | type customClaims struct { |
43 | 43 | t.Fatalf("Signer returned error: %s", err) |
44 | 44 | } |
45 | 45 | |
46 | token, ok := ctx.(context.Context).Value(JWTTokenContextKey).(string) | |
46 | token, ok := ctx.(context.Context).Value(JWTContextKey).(string) | |
47 | 47 | if !ok { |
48 | 48 | t.Fatal("Token did not exist in context") |
49 | 49 | } |
50 | 50 | |
51 | 51 | if token != expectedKey { |
52 | t.Fatalf("JWT tokens did not match: expecting %s got %s", expectedKey, token) | |
52 | t.Fatalf("JWTs did not match: expecting %s got %s", expectedKey, token) | |
53 | 53 | } |
54 | 54 | } |
55 | 55 | |
86 | 86 | } |
87 | 87 | |
88 | 88 | // Invalid Token is passed into the parser |
89 | ctx := context.WithValue(context.Background(), JWTTokenContextKey, invalidKey) | |
89 | ctx := context.WithValue(context.Background(), JWTContextKey, invalidKey) | |
90 | 90 | _, err = parser(ctx, struct{}{}) |
91 | 91 | if err == nil { |
92 | 92 | t.Error("Parser should have returned an error") |
94 | 94 | |
95 | 95 | // Invalid Method is used in the parser |
96 | 96 | badParser := NewParser(keys, invalidMethod, MapClaimsFactory)(e) |
97 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) | |
97 | ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) | |
98 | 98 | _, err = badParser(ctx, struct{}{}) |
99 | 99 | if err == nil { |
100 | 100 | t.Error("Parser should have returned an error") |
110 | 110 | } |
111 | 111 | |
112 | 112 | badParser = NewParser(invalidKeys, method, MapClaimsFactory)(e) |
113 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) | |
113 | ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) | |
114 | 114 | _, err = badParser(ctx, struct{}{}) |
115 | 115 | if err == nil { |
116 | 116 | t.Error("Parser should have returned an error") |
117 | 117 | } |
118 | 118 | |
119 | 119 | // Correct token is passed into the parser |
120 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) | |
120 | ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) | |
121 | 121 | ctx1, err := parser(ctx, struct{}{}) |
122 | 122 | if err != nil { |
123 | 123 | t.Fatalf("Parser returned error: %s", err) |
134 | 134 | |
135 | 135 | // Test for malformed token error response |
136 | 136 | parser = NewParser(keys, method, StandardClaimsFactory)(e) |
137 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, malformedKey) | |
137 | ctx = context.WithValue(context.Background(), JWTContextKey, malformedKey) | |
138 | 138 | ctx1, err = parser(ctx, struct{}{}) |
139 | 139 | if want, have := ErrTokenMalformed, err; want != have { |
140 | 140 | t.Fatalf("Expected %+v, got %+v", want, have) |
147 | 147 | if err != nil { |
148 | 148 | t.Fatalf("Unable to Sign Token: %+v", err) |
149 | 149 | } |
150 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, token) | |
150 | ctx = context.WithValue(context.Background(), JWTContextKey, token) | |
151 | 151 | ctx1, err = parser(ctx, struct{}{}) |
152 | 152 | if want, have := ErrTokenExpired, err; want != have { |
153 | 153 | t.Fatalf("Expected %+v, got %+v", want, have) |
160 | 160 | if err != nil { |
161 | 161 | t.Fatalf("Unable to Sign Token: %+v", err) |
162 | 162 | } |
163 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, token) | |
163 | ctx = context.WithValue(context.Background(), JWTContextKey, token) | |
164 | 164 | ctx1, err = parser(ctx, struct{}{}) |
165 | 165 | if want, have := ErrTokenNotActive, err; want != have { |
166 | 166 | t.Fatalf("Expected %+v, got %+v", want, have) |
168 | 168 | |
169 | 169 | // test valid standard claims token |
170 | 170 | parser = NewParser(keys, method, StandardClaimsFactory)(e) |
171 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, standardSignedKey) | |
171 | ctx = context.WithValue(context.Background(), JWTContextKey, standardSignedKey) | |
172 | 172 | ctx1, err = parser(ctx, struct{}{}) |
173 | 173 | if err != nil { |
174 | 174 | t.Fatalf("Parser returned error: %s", err) |
183 | 183 | |
184 | 184 | // test valid customized claims token |
185 | 185 | parser = NewParser(keys, method, func() jwt.Claims { return &customClaims{} })(e) |
186 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, customSignedKey) | |
186 | ctx = context.WithValue(context.Background(), JWTContextKey, customSignedKey) | |
187 | 187 | ctx1, err = parser(ctx, struct{}{}) |
188 | 188 | if err != nil { |
189 | 189 | t.Fatalf("Parser returned error: %s", err) |
204 | 204 | var ( |
205 | 205 | kf = func(token *jwt.Token) (interface{}, error) { return []byte("secret"), nil } |
206 | 206 | e = NewParser(kf, jwt.SigningMethodHS256, MapClaimsFactory)(endpoint.Nop) |
207 | key = JWTTokenContextKey | |
207 | key = JWTContextKey | |
208 | 208 | val = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E" |
209 | 209 | ctx = context.WithValue(context.Background(), key, val) |
210 | 210 | ) |
25 | 25 | return ctx |
26 | 26 | } |
27 | 27 | |
28 | return context.WithValue(ctx, JWTTokenContextKey, token) | |
28 | return context.WithValue(ctx, JWTContextKey, token) | |
29 | 29 | } |
30 | 30 | } |
31 | 31 | |
33 | 33 | // useful for clients. |
34 | 34 | func ContextToHTTP() http.RequestFunc { |
35 | 35 | return func(ctx context.Context, r *stdhttp.Request) context.Context { |
36 | token, ok := ctx.Value(JWTTokenContextKey).(string) | |
36 | token, ok := ctx.Value(JWTContextKey).(string) | |
37 | 37 | if ok { |
38 | 38 | r.Header.Add("Authorization", generateAuthHeaderFromToken(token)) |
39 | 39 | } |
53 | 53 | |
54 | 54 | token, ok := extractTokenFromAuthHeader(authHeader[0]) |
55 | 55 | if ok { |
56 | ctx = context.WithValue(ctx, JWTTokenContextKey, token) | |
56 | ctx = context.WithValue(ctx, JWTContextKey, token) | |
57 | 57 | } |
58 | 58 | |
59 | 59 | return ctx |
64 | 64 | // useful for clients. |
65 | 65 | func ContextToGRPC() grpc.ClientRequestFunc { |
66 | 66 | return func(ctx context.Context, md *metadata.MD) context.Context { |
67 | token, ok := ctx.Value(JWTTokenContextKey).(string) | |
67 | token, ok := ctx.Value(JWTContextKey).(string) | |
68 | 68 | if ok { |
69 | 69 | // capital "Key" is illegal in HTTP/2. |
70 | 70 | (*md)["authorization"] = []string{generateAuthHeaderFromToken(token)} |
76 | 76 | |
77 | 77 | func extractTokenFromAuthHeader(val string) (token string, ok bool) { |
78 | 78 | authHeaderParts := strings.Split(val, " ") |
79 | if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != bearer { | |
79 | if len(authHeaderParts) != 2 || !strings.EqualFold(authHeaderParts[0], bearer) { | |
80 | 80 | return "", false |
81 | 81 | } |
82 | 82 |
14 | 14 | // When the header doesn't exist |
15 | 15 | ctx := reqFunc(context.Background(), &http.Request{}) |
16 | 16 | |
17 | if ctx.Value(JWTTokenContextKey) != nil { | |
17 | if ctx.Value(JWTContextKey) != nil { | |
18 | 18 | t.Error("Context shouldn't contain the encoded JWT") |
19 | 19 | } |
20 | 20 | |
23 | 23 | header.Set("Authorization", "no expected auth header format value") |
24 | 24 | ctx = reqFunc(context.Background(), &http.Request{Header: header}) |
25 | 25 | |
26 | if ctx.Value(JWTTokenContextKey) != nil { | |
26 | if ctx.Value(JWTContextKey) != nil { | |
27 | 27 | t.Error("Context shouldn't contain the encoded JWT") |
28 | 28 | } |
29 | 29 | |
31 | 31 | header.Set("Authorization", generateAuthHeaderFromToken(signedKey)) |
32 | 32 | ctx = reqFunc(context.Background(), &http.Request{Header: header}) |
33 | 33 | |
34 | token := ctx.Value(JWTTokenContextKey).(string) | |
34 | token := ctx.Value(JWTContextKey).(string) | |
35 | 35 | if token != signedKey { |
36 | 36 | t.Errorf("Context doesn't contain the expected encoded token value; expected: %s, got: %s", signedKey, token) |
37 | 37 | } |
40 | 40 | func TestContextToHTTP(t *testing.T) { |
41 | 41 | reqFunc := ContextToHTTP() |
42 | 42 | |
43 | // No JWT Token is passed in the context | |
43 | // No JWT is passed in the context | |
44 | 44 | ctx := context.Background() |
45 | 45 | r := http.Request{} |
46 | 46 | reqFunc(ctx, &r) |
50 | 50 | t.Error("authorization key should not exist in metadata") |
51 | 51 | } |
52 | 52 | |
53 | // Correct JWT Token is passed in the context | |
54 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) | |
53 | // Correct JWT is passed in the context | |
54 | ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) | |
55 | 55 | r = http.Request{Header: http.Header{}} |
56 | 56 | reqFunc(ctx, &r) |
57 | 57 | |
59 | 59 | expected := generateAuthHeaderFromToken(signedKey) |
60 | 60 | |
61 | 61 | if token != expected { |
62 | t.Errorf("Authorization header does not contain the expected JWT token; expected %s, got %s", expected, token) | |
62 | t.Errorf("Authorization header does not contain the expected JWT; expected %s, got %s", expected, token) | |
63 | 63 | } |
64 | 64 | } |
65 | 65 | |
69 | 69 | |
70 | 70 | // No Authorization header is passed |
71 | 71 | ctx := reqFunc(context.Background(), md) |
72 | token := ctx.Value(JWTTokenContextKey) | |
72 | token := ctx.Value(JWTContextKey) | |
73 | 73 | if token != nil { |
74 | t.Error("Context should not contain a JWT Token") | |
74 | t.Error("Context should not contain a JWT") | |
75 | 75 | } |
76 | 76 | |
77 | 77 | // Invalid Authorization header is passed |
78 | md["authorization"] = []string{fmt.Sprintf("%s", signedKey)} | |
78 | md["authorization"] = []string{signedKey} | |
79 | 79 | ctx = reqFunc(context.Background(), md) |
80 | token = ctx.Value(JWTTokenContextKey) | |
80 | token = ctx.Value(JWTContextKey) | |
81 | 81 | if token != nil { |
82 | t.Error("Context should not contain a JWT Token") | |
82 | t.Error("Context should not contain a JWT") | |
83 | 83 | } |
84 | 84 | |
85 | 85 | // Authorization header is correct |
86 | 86 | md["authorization"] = []string{fmt.Sprintf("Bearer %s", signedKey)} |
87 | 87 | ctx = reqFunc(context.Background(), md) |
88 | token, ok := ctx.Value(JWTTokenContextKey).(string) | |
88 | token, ok := ctx.Value(JWTContextKey).(string) | |
89 | 89 | if !ok { |
90 | t.Fatal("JWT Token not passed to context correctly") | |
90 | t.Fatal("JWT not passed to context correctly") | |
91 | 91 | } |
92 | 92 | |
93 | 93 | if token != signedKey { |
94 | t.Errorf("JWT tokens did not match: expecting %s got %s", signedKey, token) | |
94 | t.Errorf("JWTs did not match: expecting %s got %s", signedKey, token) | |
95 | 95 | } |
96 | 96 | } |
97 | 97 | |
98 | 98 | func TestContextToGRPC(t *testing.T) { |
99 | 99 | reqFunc := ContextToGRPC() |
100 | 100 | |
101 | // No JWT Token is passed in the context | |
101 | // No JWT is passed in the context | |
102 | 102 | ctx := context.Background() |
103 | 103 | md := metadata.MD{} |
104 | 104 | reqFunc(ctx, &md) |
108 | 108 | t.Error("authorization key should not exist in metadata") |
109 | 109 | } |
110 | 110 | |
111 | // Correct JWT Token is passed in the context | |
112 | ctx = context.WithValue(context.Background(), JWTTokenContextKey, signedKey) | |
111 | // Correct JWT is passed in the context | |
112 | ctx = context.WithValue(context.Background(), JWTContextKey, signedKey) | |
113 | 113 | md = metadata.MD{} |
114 | 114 | reqFunc(ctx, &md) |
115 | 115 | |
116 | 116 | token, ok := md["authorization"] |
117 | 117 | if !ok { |
118 | t.Fatal("JWT Token not passed to metadata correctly") | |
118 | t.Fatal("JWT not passed to metadata correctly") | |
119 | 119 | } |
120 | 120 | |
121 | 121 | if token[0] != generateAuthHeaderFromToken(signedKey) { |
122 | t.Errorf("JWT tokens did not match: expecting %s got %s", signedKey, token[0]) | |
122 | t.Errorf("JWTs did not match: expecting %s got %s", signedKey, token[0]) | |
123 | 123 | } |
124 | 124 | } |
0 | machine: | |
1 | pre: | |
2 | - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0 | |
3 | - sudo rm -rf /usr/local/go | |
4 | - curl -sSL https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz | sudo tar xz -C /usr/local | |
5 | services: | |
6 | - docker | |
7 | ||
8 | dependencies: | |
9 | pre: | |
10 | - sudo curl -L "https://github.com/docker/compose/releases/download/1.10.0/docker-compose-linux-x86_64" -o /usr/local/bin/docker-compose | |
11 | - sudo chmod +x /usr/local/bin/docker-compose | |
12 | - docker-compose -f docker-compose-integration.yml up -d --force-recreate | |
13 | ||
14 | test: | |
15 | pre: | |
16 | - mkdir -p /home/ubuntu/.go_workspace/src/github.com/go-kit | |
17 | - mv /home/ubuntu/kit /home/ubuntu/.go_workspace/src/github.com/go-kit | |
18 | - ln -s /home/ubuntu/.go_workspace/src/github.com/go-kit/kit /home/ubuntu/kit | |
19 | - go get -t github.com/go-kit/kit/... | |
20 | override: | |
21 | - go test -v -race -tags integration github.com/go-kit/kit/...: | |
22 | environment: | |
23 | ETCD_ADDR: http://localhost:2379 | |
24 | CONSUL_ADDR: localhost:8500 | |
25 | ZK_ADDR: localhost:2181 | |
26 | EUREKA_ADDR: http://localhost:8761/eureka |
0 | comment: false |
0 | #!/usr/bin/env bash | |
1 | ||
2 | if ! type -P gover | |
3 | then | |
4 | echo gover missing: go get github.com/modocache/gover | |
5 | exit 1 | |
6 | fi | |
7 | ||
8 | if ! type -P goveralls | |
9 | then | |
10 | echo goveralls missing: go get github.com/mattn/goveralls | |
11 | exit 1 | |
12 | fi | |
13 | ||
14 | if [[ "$COVERALLS_TOKEN" == "" ]] | |
15 | then | |
16 | echo COVERALLS_TOKEN not set | |
17 | exit 1 | |
18 | fi | |
19 | ||
20 | go list ./... | grep -v '/examples/' | cut -d'/' -f 4- | while read d | |
21 | do | |
22 | cd $d | |
23 | go test -covermode count -coverprofile coverage.coverprofile | |
24 | cd - | |
25 | done | |
26 | ||
27 | gover | |
28 | goveralls -coverprofile gover.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN | |
29 | find . -name '*.coverprofile' -delete | |
30 |
0 | golang-github-go-kit-kit (0.12.0+git20220826.1.a7ba4fa-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream snapshot. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Thu, 01 Dec 2022 18:59:30 -0000 | |
5 | ||
0 | 6 | golang-github-go-kit-kit (0.6.0-4) unstable; urgency=medium |
1 | 7 | |
2 | 8 | [ Debian Janitor ] |
0 | 0 | version: '2' |
1 | 1 | services: |
2 | 2 | etcd: |
3 | image: quay.io/coreos/etcd | |
3 | image: gcr.io/etcd-development/etcd:v3.5.0 | |
4 | 4 | ports: |
5 | 5 | - "2379:2379" |
6 | command: /usr/local/bin/etcd -advertise-client-urls http://0.0.0.0:2379,http://0.0.0.0:4001 -listen-client-urls "http://0.0.0.0:2379,http://0.0.0.0:4001" | |
6 | environment: | |
7 | ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379 | |
8 | ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379 | |
9 | ||
7 | 10 | consul: |
8 | image: progrium/consul | |
11 | image: consul:1.7 | |
9 | 12 | ports: |
10 | 13 | - "8500:8500" |
11 | command: -server -bootstrap | |
14 | ||
12 | 15 | zk: |
13 | image: zookeeper | |
16 | image: zookeeper:3.5 | |
14 | 17 | ports: |
15 | 18 | - "2181:2181" |
19 | ||
16 | 20 | eureka: |
17 | 21 | image: springcloud/eureka |
18 | 22 | environment: |
25 | 25 | return outer(next) |
26 | 26 | } |
27 | 27 | } |
28 | ||
29 | // Failer may be implemented by Go kit response types that contain business | |
30 | // logic error details. If Failed returns a non-nil error, the Go kit transport | |
31 | // layer may interpret this as a business logic error, and may encode it | |
32 | // differently than a regular, successful response. | |
33 | // | |
34 | // It's not necessary for your response types to implement Failer, but it may | |
35 | // help for more sophisticated use cases. The addsvc example shows how Failer | |
36 | // should be used by a complete application. | |
37 | type Failer interface { | |
38 | Failed() error | |
39 | } |
0 | 0 | # Examples |
1 | 1 | |
2 | For more information about these examples, | |
3 | including a walkthrough of the stringsvc example, | |
4 | see [gokit.io/examples](https://gokit.io/examples). | |
2 | Examples have been relocated to a separate repository: https://github.com/go-kit/examples |
0 | # addsvc | |
1 | ||
2 | addsvc is an example microservice which takes full advantage of most of Go | |
3 | kit's features, including both service- and transport-level middlewares, | |
4 | speaking multiple transports simultaneously, distributed tracing, and rich | |
5 | error definitions. The server binary is available in cmd/addsvc. The client | |
6 | binary is available in cmd/addcli. | |
7 | ||
8 | Finally, the addtransport package provides both server and clients for each | |
9 | supported transport. The client structs bake-in certain middlewares, in order to | |
10 | demonstrate the _client library pattern_. But beware: client libraries are | |
11 | generally a bad idea, because they easily lead to the | |
12 | [distributed monolith antipattern](https://www.microservices.com/talks/dont-build-a-distributed-monolith/). | |
13 | If you don't _know_ you need to use one in your organization, it's probably best | |
14 | avoided: prefer moving that logic to consumers, and relying on | |
15 | [contract testing](https://docs.pact.io/best_practices/contract_tests_not_functional_tests.html) | |
16 | to detect incompatibilities. |
0 | package main | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "flag" | |
5 | "fmt" | |
6 | "os" | |
7 | "strconv" | |
8 | "text/tabwriter" | |
9 | "time" | |
10 | ||
11 | "google.golang.org/grpc" | |
12 | ||
13 | "github.com/apache/thrift/lib/go/thrift" | |
14 | lightstep "github.com/lightstep/lightstep-tracer-go" | |
15 | stdopentracing "github.com/opentracing/opentracing-go" | |
16 | zipkin "github.com/openzipkin/zipkin-go-opentracing" | |
17 | "sourcegraph.com/sourcegraph/appdash" | |
18 | appdashot "sourcegraph.com/sourcegraph/appdash/opentracing" | |
19 | ||
20 | "github.com/go-kit/kit/log" | |
21 | ||
22 | "github.com/go-kit/kit/examples/addsvc/pkg/addservice" | |
23 | "github.com/go-kit/kit/examples/addsvc/pkg/addtransport" | |
24 | addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" | |
25 | ) | |
26 | ||
27 | func main() { | |
28 | // The addcli presumes no service discovery system, and expects users to | |
29 | // provide the direct address of an addsvc. This presumption is reflected in | |
30 | // the addcli binary and the client packages: the -transport.addr flags | |
31 | // and various client constructors both expect host:port strings. For an | |
32 | // example service with a client built on top of a service discovery system, | |
33 | // see profilesvc. | |
34 | fs := flag.NewFlagSet("addcli", flag.ExitOnError) | |
35 | var ( | |
36 | httpAddr = fs.String("http-addr", "", "HTTP address of addsvc") | |
37 | grpcAddr = fs.String("grpc-addr", "", "gRPC address of addsvc") | |
38 | thriftAddr = fs.String("thrift-addr", "", "Thrift address of addsvc") | |
39 | thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson") | |
40 | thriftBuffer = fs.Int("thrift-buffer", 0, "0 for unbuffered") | |
41 | thriftFramed = fs.Bool("thrift-framed", false, "true to enable framing") | |
42 | zipkinURL = fs.String("zipkin-url", "", "Enable Zipkin tracing via a collector URL e.g. http://localhost:9411/api/v1/spans") | |
43 | lightstepToken = flag.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token") | |
44 | appdashAddr = flag.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port") | |
45 | method = fs.String("method", "sum", "sum, concat") | |
46 | ) | |
47 | fs.Usage = usageFor(fs, os.Args[0]+" [flags] <a> <b>") | |
48 | fs.Parse(os.Args[1:]) | |
49 | if len(fs.Args()) != 2 { | |
50 | fs.Usage() | |
51 | os.Exit(1) | |
52 | } | |
53 | ||
54 | // This is a demonstration client, which supports multiple tracers. | |
55 | // Your clients will probably just use one tracer. | |
56 | var tracer stdopentracing.Tracer | |
57 | { | |
58 | if *zipkinURL != "" { | |
59 | collector, err := zipkin.NewHTTPCollector(*zipkinURL) | |
60 | if err != nil { | |
61 | fmt.Fprintln(os.Stderr, err.Error()) | |
62 | os.Exit(1) | |
63 | } | |
64 | defer collector.Close() | |
65 | var ( | |
66 | debug = false | |
67 | hostPort = "localhost:80" | |
68 | serviceName = "addsvc" | |
69 | ) | |
70 | recorder := zipkin.NewRecorder(collector, debug, hostPort, serviceName) | |
71 | tracer, err = zipkin.NewTracer(recorder) | |
72 | if err != nil { | |
73 | fmt.Fprintln(os.Stderr, err.Error()) | |
74 | os.Exit(1) | |
75 | } | |
76 | } else if *lightstepToken != "" { | |
77 | tracer = lightstep.NewTracer(lightstep.Options{ | |
78 | AccessToken: *lightstepToken, | |
79 | }) | |
80 | defer lightstep.FlushLightStepTracer(tracer) | |
81 | } else if *appdashAddr != "" { | |
82 | tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr)) | |
83 | } else { | |
84 | tracer = stdopentracing.GlobalTracer() // no-op | |
85 | } | |
86 | } | |
87 | ||
88 | // This is a demonstration client, which supports multiple transports. | |
89 | // Your clients will probably just define and stick with 1 transport. | |
90 | var ( | |
91 | svc addservice.Service | |
92 | err error | |
93 | ) | |
94 | if *httpAddr != "" { | |
95 | svc, err = addtransport.NewHTTPClient(*httpAddr, tracer, log.NewNopLogger()) | |
96 | } else if *grpcAddr != "" { | |
97 | conn, err := grpc.Dial(*grpcAddr, grpc.WithInsecure(), grpc.WithTimeout(time.Second)) | |
98 | if err != nil { | |
99 | fmt.Fprintf(os.Stderr, "error: %v", err) | |
100 | os.Exit(1) | |
101 | } | |
102 | defer conn.Close() | |
103 | svc = addtransport.NewGRPCClient(conn, tracer, log.NewNopLogger()) | |
104 | } else if *thriftAddr != "" { | |
105 | // It's necessary to do all of this construction in the func main, | |
106 | // because (among other reasons) we need to control the lifecycle of the | |
107 | // Thrift transport, i.e. close it eventually. | |
108 | var protocolFactory thrift.TProtocolFactory | |
109 | switch *thriftProtocol { | |
110 | case "compact": | |
111 | protocolFactory = thrift.NewTCompactProtocolFactory() | |
112 | case "simplejson": | |
113 | protocolFactory = thrift.NewTSimpleJSONProtocolFactory() | |
114 | case "json": | |
115 | protocolFactory = thrift.NewTJSONProtocolFactory() | |
116 | case "binary", "": | |
117 | protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() | |
118 | default: | |
119 | fmt.Fprintf(os.Stderr, "error: invalid protocol %q\n", *thriftProtocol) | |
120 | os.Exit(1) | |
121 | } | |
122 | var transportFactory thrift.TTransportFactory | |
123 | if *thriftBuffer > 0 { | |
124 | transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer) | |
125 | } else { | |
126 | transportFactory = thrift.NewTTransportFactory() | |
127 | } | |
128 | if *thriftFramed { | |
129 | transportFactory = thrift.NewTFramedTransportFactory(transportFactory) | |
130 | } | |
131 | transportSocket, err := thrift.NewTSocket(*thriftAddr) | |
132 | if err != nil { | |
133 | fmt.Fprintf(os.Stderr, "error: %v\n", err) | |
134 | os.Exit(1) | |
135 | } | |
136 | transport, err := transportFactory.GetTransport(transportSocket) | |
137 | if err != nil { | |
138 | fmt.Fprintf(os.Stderr, "error: %v\n", err) | |
139 | os.Exit(1) | |
140 | } | |
141 | if err := transport.Open(); err != nil { | |
142 | fmt.Fprintf(os.Stderr, "error: %v\n", err) | |
143 | os.Exit(1) | |
144 | } | |
145 | defer transport.Close() | |
146 | client := addthrift.NewAddServiceClientFactory(transport, protocolFactory) | |
147 | svc = addtransport.NewThriftClient(client) | |
148 | } else { | |
149 | fmt.Fprintf(os.Stderr, "error: no remote address specified\n") | |
150 | os.Exit(1) | |
151 | } | |
152 | if err != nil { | |
153 | fmt.Fprintf(os.Stderr, "error: %v\n", err) | |
154 | os.Exit(1) | |
155 | } | |
156 | ||
157 | switch *method { | |
158 | case "sum": | |
159 | a, _ := strconv.ParseInt(fs.Args()[0], 10, 64) | |
160 | b, _ := strconv.ParseInt(fs.Args()[1], 10, 64) | |
161 | v, err := svc.Sum(context.Background(), int(a), int(b)) | |
162 | if err != nil { | |
163 | fmt.Fprintf(os.Stderr, "error: %v\n", err) | |
164 | os.Exit(1) | |
165 | } | |
166 | fmt.Fprintf(os.Stdout, "%d + %d = %d\n", a, b, v) | |
167 | ||
168 | case "concat": | |
169 | a := fs.Args()[0] | |
170 | b := fs.Args()[1] | |
171 | v, err := svc.Concat(context.Background(), a, b) | |
172 | if err != nil { | |
173 | fmt.Fprintf(os.Stderr, "error: %v\n", err) | |
174 | os.Exit(1) | |
175 | } | |
176 | fmt.Fprintf(os.Stdout, "%q + %q = %q\n", a, b, v) | |
177 | ||
178 | default: | |
179 | fmt.Fprintf(os.Stderr, "error: invalid method %q\n", method) | |
180 | os.Exit(1) | |
181 | } | |
182 | } | |
183 | ||
184 | func usageFor(fs *flag.FlagSet, short string) func() { | |
185 | return func() { | |
186 | fmt.Fprintf(os.Stderr, "USAGE\n") | |
187 | fmt.Fprintf(os.Stderr, " %s\n", short) | |
188 | fmt.Fprintf(os.Stderr, "\n") | |
189 | fmt.Fprintf(os.Stderr, "FLAGS\n") | |
190 | w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0) | |
191 | fs.VisitAll(func(f *flag.Flag) { | |
192 | fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage) | |
193 | }) | |
194 | w.Flush() | |
195 | fmt.Fprintf(os.Stderr, "\n") | |
196 | } | |
197 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "flag" | |
4 | "fmt" | |
5 | "net" | |
6 | "net/http" | |
7 | "os" | |
8 | "os/signal" | |
9 | "syscall" | |
10 | "text/tabwriter" | |
11 | ||
12 | "github.com/apache/thrift/lib/go/thrift" | |
13 | lightstep "github.com/lightstep/lightstep-tracer-go" | |
14 | "github.com/oklog/oklog/pkg/group" | |
15 | stdopentracing "github.com/opentracing/opentracing-go" | |
16 | zipkin "github.com/openzipkin/zipkin-go-opentracing" | |
17 | stdprometheus "github.com/prometheus/client_golang/prometheus" | |
18 | "github.com/prometheus/client_golang/prometheus/promhttp" | |
19 | "google.golang.org/grpc" | |
20 | "sourcegraph.com/sourcegraph/appdash" | |
21 | appdashot "sourcegraph.com/sourcegraph/appdash/opentracing" | |
22 | ||
23 | "github.com/go-kit/kit/log" | |
24 | "github.com/go-kit/kit/metrics" | |
25 | "github.com/go-kit/kit/metrics/prometheus" | |
26 | ||
27 | addpb "github.com/go-kit/kit/examples/addsvc/pb" | |
28 | "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" | |
29 | "github.com/go-kit/kit/examples/addsvc/pkg/addservice" | |
30 | "github.com/go-kit/kit/examples/addsvc/pkg/addtransport" | |
31 | addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" | |
32 | ) | |
33 | ||
34 | func main() { | |
35 | // Define our flags. Your service probably won't need to bind listeners for | |
36 | // *all* supported transports, or support both Zipkin and LightStep, and so | |
37 | // on, but we do it here for demonstration purposes. | |
38 | fs := flag.NewFlagSet("addsvc", flag.ExitOnError) | |
39 | var ( | |
40 | debugAddr = fs.String("debug.addr", ":8080", "Debug and metrics listen address") | |
41 | httpAddr = fs.String("http-addr", ":8081", "HTTP listen address") | |
42 | grpcAddr = fs.String("grpc-addr", ":8082", "gRPC listen address") | |
43 | thriftAddr = fs.String("thrift-addr", ":8083", "Thrift listen address") | |
44 | thriftProtocol = fs.String("thrift-protocol", "binary", "binary, compact, json, simplejson") | |
45 | thriftBuffer = fs.Int("thrift-buffer", 0, "0 for unbuffered") | |
46 | thriftFramed = fs.Bool("thrift-framed", false, "true to enable framing") | |
47 | zipkinURL = fs.String("zipkin-url", "", "Enable Zipkin tracing via a collector URL e.g. http://localhost:9411/api/v1/spans") | |
48 | lightstepToken = flag.String("lightstep-token", "", "Enable LightStep tracing via a LightStep access token") | |
49 | appdashAddr = flag.String("appdash-addr", "", "Enable Appdash tracing via an Appdash server host:port") | |
50 | ) | |
51 | fs.Usage = usageFor(fs, os.Args[0]+" [flags]") | |
52 | fs.Parse(os.Args[1:]) | |
53 | ||
54 | // Create a single logger, which we'll use and give to other components. | |
55 | var logger log.Logger | |
56 | { | |
57 | logger = log.NewLogfmtLogger(os.Stderr) | |
58 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) | |
59 | logger = log.With(logger, "caller", log.DefaultCaller) | |
60 | } | |
61 | ||
62 | // Determine which tracer to use. We'll pass the tracer to all the | |
63 | // components that use it, as a dependency. | |
64 | var tracer stdopentracing.Tracer | |
65 | { | |
66 | if *zipkinURL != "" { | |
67 | logger.Log("tracer", "Zipkin", "URL", *zipkinURL) | |
68 | collector, err := zipkin.NewHTTPCollector(*zipkinURL) | |
69 | if err != nil { | |
70 | logger.Log("err", err) | |
71 | os.Exit(1) | |
72 | } | |
73 | defer collector.Close() | |
74 | var ( | |
75 | debug = false | |
76 | hostPort = "localhost:80" | |
77 | serviceName = "addsvc" | |
78 | ) | |
79 | recorder := zipkin.NewRecorder(collector, debug, hostPort, serviceName) | |
80 | tracer, err = zipkin.NewTracer(recorder) | |
81 | if err != nil { | |
82 | logger.Log("err", err) | |
83 | os.Exit(1) | |
84 | } | |
85 | } else if *lightstepToken != "" { | |
86 | logger.Log("tracer", "LightStep") // probably don't want to print out the token :) | |
87 | tracer = lightstep.NewTracer(lightstep.Options{ | |
88 | AccessToken: *lightstepToken, | |
89 | }) | |
90 | defer lightstep.FlushLightStepTracer(tracer) | |
91 | } else if *appdashAddr != "" { | |
92 | logger.Log("tracer", "Appdash", "addr", *appdashAddr) | |
93 | tracer = appdashot.NewTracer(appdash.NewRemoteCollector(*appdashAddr)) | |
94 | } else { | |
95 | logger.Log("tracer", "none") | |
96 | tracer = stdopentracing.GlobalTracer() // no-op | |
97 | } | |
98 | } | |
99 | ||
100 | // Create the (sparse) metrics we'll use in the service. They, too, are | |
101 | // dependencies that we pass to components that use them. | |
102 | var ints, chars metrics.Counter | |
103 | { | |
104 | // Business-level metrics. | |
105 | ints = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ | |
106 | Namespace: "example", | |
107 | Subsystem: "addsvc", | |
108 | Name: "integers_summed", | |
109 | Help: "Total count of integers summed via the Sum method.", | |
110 | }, []string{}) | |
111 | chars = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ | |
112 | Namespace: "example", | |
113 | Subsystem: "addsvc", | |
114 | Name: "characters_concatenated", | |
115 | Help: "Total count of characters concatenated via the Concat method.", | |
116 | }, []string{}) | |
117 | } | |
118 | var duration metrics.Histogram | |
119 | { | |
120 | // Endpoint-level metrics. | |
121 | duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ | |
122 | Namespace: "example", | |
123 | Subsystem: "addsvc", | |
124 | Name: "request_duration_seconds", | |
125 | Help: "Request duration in seconds.", | |
126 | }, []string{"method", "success"}) | |
127 | } | |
128 | http.DefaultServeMux.Handle("/metrics", promhttp.Handler()) | |
129 | ||
130 | // Build the layers of the service "onion" from the inside out. First, the | |
131 | // business logic service; then, the set of endpoints that wrap the service; | |
132 | // and finally, a series of concrete transport adapters. The adapters, like | |
133 | // the HTTP handler or the gRPC server, are the bridge between Go kit and | |
134 | // the interfaces that the transports expect. Note that we're not binding | |
135 | // them to ports or anything yet; we'll do that next. | |
136 | var ( | |
137 | service = addservice.New(logger, ints, chars) | |
138 | endpoints = addendpoint.New(service, logger, duration, tracer) | |
139 | httpHandler = addtransport.NewHTTPHandler(endpoints, tracer, logger) | |
140 | grpcServer = addtransport.NewGRPCServer(endpoints, tracer, logger) | |
141 | thriftServer = addtransport.NewThriftServer(endpoints) | |
142 | ) | |
143 | ||
144 | // Now we're to the part of the func main where we want to start actually | |
145 | // running things, like servers bound to listeners to receive connections. | |
146 | // | |
147 | // The method is the same for each component: add a new actor to the group | |
148 | // struct, which is a combination of 2 anonymous functions: the first | |
149 | // function actually runs the component, and the second function should | |
150 | // interrupt the first function and cause it to return. It's in these | |
151 | // functions that we actually bind the Go kit server/handler structs to the | |
152 | // concrete transports and run them. | |
153 | // | |
154 | // Putting each component into its own block is mostly for aesthetics: it | |
155 | // clearly demarcates the scope in which each listener/socket may be used. | |
156 | var g group.Group | |
157 | { | |
158 | // The debug listener mounts the http.DefaultServeMux, and serves up | |
159 | // stuff like the Prometheus metrics route, the Go debug and profiling | |
160 | // routes, and so on. | |
161 | debugListener, err := net.Listen("tcp", *debugAddr) | |
162 | if err != nil { | |
163 | logger.Log("transport", "debug/HTTP", "during", "Listen", "err", err) | |
164 | os.Exit(1) | |
165 | } | |
166 | g.Add(func() error { | |
167 | logger.Log("transport", "debug/HTTP", "addr", *debugAddr) | |
168 | return http.Serve(debugListener, http.DefaultServeMux) | |
169 | }, func(error) { | |
170 | debugListener.Close() | |
171 | }) | |
172 | } | |
173 | { | |
174 | // The HTTP listener mounts the Go kit HTTP handler we created. | |
175 | httpListener, err := net.Listen("tcp", *httpAddr) | |
176 | if err != nil { | |
177 | logger.Log("transport", "HTTP", "during", "Listen", "err", err) | |
178 | os.Exit(1) | |
179 | } | |
180 | g.Add(func() error { | |
181 | logger.Log("transport", "HTTP", "addr", *httpAddr) | |
182 | return http.Serve(httpListener, httpHandler) | |
183 | }, func(error) { | |
184 | httpListener.Close() | |
185 | }) | |
186 | } | |
187 | { | |
188 | // The gRPC listener mounts the Go kit gRPC server we created. | |
189 | grpcListener, err := net.Listen("tcp", *grpcAddr) | |
190 | if err != nil { | |
191 | logger.Log("transport", "gRPC", "during", "Listen", "err", err) | |
192 | os.Exit(1) | |
193 | } | |
194 | g.Add(func() error { | |
195 | logger.Log("transport", "gRPC", "addr", *grpcAddr) | |
196 | baseServer := grpc.NewServer() | |
197 | addpb.RegisterAddServer(baseServer, grpcServer) | |
198 | return baseServer.Serve(grpcListener) | |
199 | }, func(error) { | |
200 | grpcListener.Close() | |
201 | }) | |
202 | } | |
203 | { | |
204 | // The Thrift socket mounts the Go kit Thrift server we created earlier. | |
205 | // There's a lot of boilerplate involved here, related to configuring | |
206 | // the protocol and transport; blame Thrift. | |
207 | thriftSocket, err := thrift.NewTServerSocket(*thriftAddr) | |
208 | if err != nil { | |
209 | logger.Log("transport", "Thrift", "during", "Listen", "err", err) | |
210 | os.Exit(1) | |
211 | } | |
212 | g.Add(func() error { | |
213 | logger.Log("transport", "Thrift", "addr", *thriftAddr) | |
214 | var protocolFactory thrift.TProtocolFactory | |
215 | switch *thriftProtocol { | |
216 | case "binary": | |
217 | protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() | |
218 | case "compact": | |
219 | protocolFactory = thrift.NewTCompactProtocolFactory() | |
220 | case "json": | |
221 | protocolFactory = thrift.NewTJSONProtocolFactory() | |
222 | case "simplejson": | |
223 | protocolFactory = thrift.NewTSimpleJSONProtocolFactory() | |
224 | default: | |
225 | return fmt.Errorf("invalid Thrift protocol %q", *thriftProtocol) | |
226 | } | |
227 | var transportFactory thrift.TTransportFactory | |
228 | if *thriftBuffer > 0 { | |
229 | transportFactory = thrift.NewTBufferedTransportFactory(*thriftBuffer) | |
230 | } else { | |
231 | transportFactory = thrift.NewTTransportFactory() | |
232 | } | |
233 | if *thriftFramed { | |
234 | transportFactory = thrift.NewTFramedTransportFactory(transportFactory) | |
235 | } | |
236 | return thrift.NewTSimpleServer4( | |
237 | addthrift.NewAddServiceProcessor(thriftServer), | |
238 | thriftSocket, | |
239 | transportFactory, | |
240 | protocolFactory, | |
241 | ).Serve() | |
242 | }, func(error) { | |
243 | thriftSocket.Close() | |
244 | }) | |
245 | } | |
246 | { | |
247 | // This function just sits and waits for ctrl-C. | |
248 | cancelInterrupt := make(chan struct{}) | |
249 | g.Add(func() error { | |
250 | c := make(chan os.Signal, 1) | |
251 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) | |
252 | select { | |
253 | case sig := <-c: | |
254 | return fmt.Errorf("received signal %s", sig) | |
255 | case <-cancelInterrupt: | |
256 | return nil | |
257 | } | |
258 | }, func(error) { | |
259 | close(cancelInterrupt) | |
260 | }) | |
261 | } | |
262 | logger.Log("exit", g.Run()) | |
263 | } | |
264 | ||
265 | func usageFor(fs *flag.FlagSet, short string) func() { | |
266 | return func() { | |
267 | fmt.Fprintf(os.Stderr, "USAGE\n") | |
268 | fmt.Fprintf(os.Stderr, " %s\n", short) | |
269 | fmt.Fprintf(os.Stderr, "\n") | |
270 | fmt.Fprintf(os.Stderr, "FLAGS\n") | |
271 | w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0) | |
272 | fs.VisitAll(func(f *flag.Flag) { | |
273 | fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage) | |
274 | }) | |
275 | w.Flush() | |
276 | fmt.Fprintf(os.Stderr, "\n") | |
277 | } | |
278 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "fmt" | |
4 | "net/http" | |
5 | "os" | |
6 | "strings" | |
7 | "testing" | |
8 | ||
9 | "github.com/pact-foundation/pact-go/dsl" | |
10 | ) | |
11 | ||
12 | func TestPactStringsvcUppercase(t *testing.T) { | |
13 | if os.Getenv("WRITE_PACTS") == "" { | |
14 | t.Skip("skipping Pact contracts; set WRITE_PACTS environment variable to enable") | |
15 | } | |
16 | ||
17 | pact := dsl.Pact{ | |
18 | Port: 6666, | |
19 | Consumer: "addsvc", | |
20 | Provider: "stringsvc", | |
21 | } | |
22 | defer pact.Teardown() | |
23 | ||
24 | pact.AddInteraction(). | |
25 | UponReceiving("stringsvc uppercase"). | |
26 | WithRequest(dsl.Request{ | |
27 | Headers: map[string]string{"Content-Type": "application/json; charset=utf-8"}, | |
28 | Method: "POST", | |
29 | Path: "/uppercase", | |
30 | Body: `{"s":"foo"}`, | |
31 | }). | |
32 | WillRespondWith(dsl.Response{ | |
33 | Status: 200, | |
34 | Headers: map[string]string{"Content-Type": "application/json; charset=utf-8"}, | |
35 | Body: `{"v":"FOO"}`, | |
36 | }) | |
37 | ||
38 | if err := pact.Verify(func() error { | |
39 | u := fmt.Sprintf("http://localhost:%d/uppercase", pact.Server.Port) | |
40 | req, err := http.NewRequest("POST", u, strings.NewReader(`{"s":"foo"}`)) | |
41 | if err != nil { | |
42 | return err | |
43 | } | |
44 | req.Header.Set("Content-Type", "application/json; charset=utf-8") | |
45 | if _, err = http.DefaultClient.Do(req); err != nil { | |
46 | return err | |
47 | } | |
48 | return nil | |
49 | }); err != nil { | |
50 | t.Fatal(err) | |
51 | } | |
52 | ||
53 | pact.WritePact() | |
54 | } |
0 | package main | |
1 | ||
2 | import ( | |
3 | "io/ioutil" | |
4 | "net/http" | |
5 | "net/http/httptest" | |
6 | "strings" | |
7 | "testing" | |
8 | ||
9 | "github.com/opentracing/opentracing-go" | |
10 | ||
11 | "github.com/go-kit/kit/log" | |
12 | "github.com/go-kit/kit/metrics/discard" | |
13 | ||
14 | "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" | |
15 | "github.com/go-kit/kit/examples/addsvc/pkg/addservice" | |
16 | "github.com/go-kit/kit/examples/addsvc/pkg/addtransport" | |
17 | ) | |
18 | ||
19 | func TestHTTP(t *testing.T) { | |
20 | svc := addservice.New(log.NewNopLogger(), discard.NewCounter(), discard.NewCounter()) | |
21 | eps := addendpoint.New(svc, log.NewNopLogger(), discard.NewHistogram(), opentracing.GlobalTracer()) | |
22 | mux := addtransport.NewHTTPHandler(eps, opentracing.GlobalTracer(), log.NewNopLogger()) | |
23 | srv := httptest.NewServer(mux) | |
24 | defer srv.Close() | |
25 | ||
26 | for _, testcase := range []struct { | |
27 | method, url, body, want string | |
28 | }{ | |
29 | {"GET", srv.URL + "/concat", `{"a":"1","b":"2"}`, `{"v":"12"}`}, | |
30 | {"GET", srv.URL + "/sum", `{"a":1,"b":2}`, `{"v":3}`}, | |
31 | } { | |
32 | req, _ := http.NewRequest(testcase.method, testcase.url, strings.NewReader(testcase.body)) | |
33 | resp, _ := http.DefaultClient.Do(req) | |
34 | body, _ := ioutil.ReadAll(resp.Body) | |
35 | if want, have := testcase.want, strings.TrimSpace(string(body)); want != have { | |
36 | t.Errorf("%s %s %s: want %q, have %q", testcase.method, testcase.url, testcase.body, want, have) | |
37 | } | |
38 | } | |
39 | } |
0 | // Code generated by protoc-gen-go. DO NOT EDIT. | |
1 | // source: addsvc.proto | |
2 | ||
3 | /* | |
4 | Package pb is a generated protocol buffer package. | |
5 | ||
6 | It is generated from these files: | |
7 | addsvc.proto | |
8 | ||
9 | It has these top-level messages: | |
10 | SumRequest | |
11 | SumReply | |
12 | ConcatRequest | |
13 | ConcatReply | |
14 | */ | |
15 | package pb | |
16 | ||
17 | import proto "github.com/golang/protobuf/proto" | |
18 | import fmt "fmt" | |
19 | import math "math" | |
20 | ||
21 | import ( | |
22 | context "golang.org/x/net/context" | |
23 | grpc "google.golang.org/grpc" | |
24 | ) | |
25 | ||
26 | // Reference imports to suppress errors if they are not otherwise used. | |
27 | var _ = proto.Marshal | |
28 | var _ = fmt.Errorf | |
29 | var _ = math.Inf | |
30 | ||
31 | // This is a compile-time assertion to ensure that this generated file | |
32 | // is compatible with the proto package it is being compiled against. | |
33 | // A compilation error at this line likely means your copy of the | |
34 | // proto package needs to be updated. | |
35 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package | |
36 | ||
37 | // The sum request contains two parameters. | |
38 | type SumRequest struct { | |
39 | A int64 `protobuf:"varint,1,opt,name=a" json:"a,omitempty"` | |
40 | B int64 `protobuf:"varint,2,opt,name=b" json:"b,omitempty"` | |
41 | } | |
42 | ||
43 | func (m *SumRequest) Reset() { *m = SumRequest{} } | |
44 | func (m *SumRequest) String() string { return proto.CompactTextString(m) } | |
45 | func (*SumRequest) ProtoMessage() {} | |
46 | func (*SumRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } | |
47 | ||
48 | func (m *SumRequest) GetA() int64 { | |
49 | if m != nil { | |
50 | return m.A | |
51 | } | |
52 | return 0 | |
53 | } | |
54 | ||
55 | func (m *SumRequest) GetB() int64 { | |
56 | if m != nil { | |
57 | return m.B | |
58 | } | |
59 | return 0 | |
60 | } | |
61 | ||
62 | // The sum response contains the result of the calculation. | |
63 | type SumReply struct { | |
64 | V int64 `protobuf:"varint,1,opt,name=v" json:"v,omitempty"` | |
65 | Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` | |
66 | } | |
67 | ||
68 | func (m *SumReply) Reset() { *m = SumReply{} } | |
69 | func (m *SumReply) String() string { return proto.CompactTextString(m) } | |
70 | func (*SumReply) ProtoMessage() {} | |
71 | func (*SumReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } | |
72 | ||
73 | func (m *SumReply) GetV() int64 { | |
74 | if m != nil { | |
75 | return m.V | |
76 | } | |
77 | return 0 | |
78 | } | |
79 | ||
80 | func (m *SumReply) GetErr() string { | |
81 | if m != nil { | |
82 | return m.Err | |
83 | } | |
84 | return "" | |
85 | } | |
86 | ||
87 | // The Concat request contains two parameters. | |
88 | type ConcatRequest struct { | |
89 | A string `protobuf:"bytes,1,opt,name=a" json:"a,omitempty"` | |
90 | B string `protobuf:"bytes,2,opt,name=b" json:"b,omitempty"` | |
91 | } | |
92 | ||
93 | func (m *ConcatRequest) Reset() { *m = ConcatRequest{} } | |
94 | func (m *ConcatRequest) String() string { return proto.CompactTextString(m) } | |
95 | func (*ConcatRequest) ProtoMessage() {} | |
96 | func (*ConcatRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } | |
97 | ||
98 | func (m *ConcatRequest) GetA() string { | |
99 | if m != nil { | |
100 | return m.A | |
101 | } | |
102 | return "" | |
103 | } | |
104 | ||
105 | func (m *ConcatRequest) GetB() string { | |
106 | if m != nil { | |
107 | return m.B | |
108 | } | |
109 | return "" | |
110 | } | |
111 | ||
112 | // The Concat response contains the result of the concatenation. | |
113 | type ConcatReply struct { | |
114 | V string `protobuf:"bytes,1,opt,name=v" json:"v,omitempty"` | |
115 | Err string `protobuf:"bytes,2,opt,name=err" json:"err,omitempty"` | |
116 | } | |
117 | ||
118 | func (m *ConcatReply) Reset() { *m = ConcatReply{} } | |
119 | func (m *ConcatReply) String() string { return proto.CompactTextString(m) } | |
120 | func (*ConcatReply) ProtoMessage() {} | |
121 | func (*ConcatReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } | |
122 | ||
123 | func (m *ConcatReply) GetV() string { | |
124 | if m != nil { | |
125 | return m.V | |
126 | } | |
127 | return "" | |
128 | } | |
129 | ||
130 | func (m *ConcatReply) GetErr() string { | |
131 | if m != nil { | |
132 | return m.Err | |
133 | } | |
134 | return "" | |
135 | } | |
136 | ||
137 | func init() { | |
138 | proto.RegisterType((*SumRequest)(nil), "pb.SumRequest") | |
139 | proto.RegisterType((*SumReply)(nil), "pb.SumReply") | |
140 | proto.RegisterType((*ConcatRequest)(nil), "pb.ConcatRequest") | |
141 | proto.RegisterType((*ConcatReply)(nil), "pb.ConcatReply") | |
142 | } | |
143 | ||
144 | // Reference imports to suppress errors if they are not otherwise used. | |
145 | var _ context.Context | |
146 | var _ grpc.ClientConn | |
147 | ||
148 | // This is a compile-time assertion to ensure that this generated file | |
149 | // is compatible with the grpc package it is being compiled against. | |
150 | const _ = grpc.SupportPackageIsVersion4 | |
151 | ||
152 | // Client API for Add service | |
153 | ||
154 | type AddClient interface { | |
155 | // Sums two integers. | |
156 | Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error) | |
157 | // Concatenates two strings | |
158 | Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error) | |
159 | } | |
160 | ||
161 | type addClient struct { | |
162 | cc *grpc.ClientConn | |
163 | } | |
164 | ||
165 | func NewAddClient(cc *grpc.ClientConn) AddClient { | |
166 | return &addClient{cc} | |
167 | } | |
168 | ||
169 | func (c *addClient) Sum(ctx context.Context, in *SumRequest, opts ...grpc.CallOption) (*SumReply, error) { | |
170 | out := new(SumReply) | |
171 | err := grpc.Invoke(ctx, "/pb.Add/Sum", in, out, c.cc, opts...) | |
172 | if err != nil { | |
173 | return nil, err | |
174 | } | |
175 | return out, nil | |
176 | } | |
177 | ||
178 | func (c *addClient) Concat(ctx context.Context, in *ConcatRequest, opts ...grpc.CallOption) (*ConcatReply, error) { | |
179 | out := new(ConcatReply) | |
180 | err := grpc.Invoke(ctx, "/pb.Add/Concat", in, out, c.cc, opts...) | |
181 | if err != nil { | |
182 | return nil, err | |
183 | } | |
184 | return out, nil | |
185 | } | |
186 | ||
187 | // Server API for Add service | |
188 | ||
189 | type AddServer interface { | |
190 | // Sums two integers. | |
191 | Sum(context.Context, *SumRequest) (*SumReply, error) | |
192 | // Concatenates two strings | |
193 | Concat(context.Context, *ConcatRequest) (*ConcatReply, error) | |
194 | } | |
195 | ||
196 | func RegisterAddServer(s *grpc.Server, srv AddServer) { | |
197 | s.RegisterService(&_Add_serviceDesc, srv) | |
198 | } | |
199 | ||
200 | func _Add_Sum_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | |
201 | in := new(SumRequest) | |
202 | if err := dec(in); err != nil { | |
203 | return nil, err | |
204 | } | |
205 | if interceptor == nil { | |
206 | return srv.(AddServer).Sum(ctx, in) | |
207 | } | |
208 | info := &grpc.UnaryServerInfo{ | |
209 | Server: srv, | |
210 | FullMethod: "/pb.Add/Sum", | |
211 | } | |
212 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { | |
213 | return srv.(AddServer).Sum(ctx, req.(*SumRequest)) | |
214 | } | |
215 | return interceptor(ctx, in, info, handler) | |
216 | } | |
217 | ||
218 | func _Add_Concat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { | |
219 | in := new(ConcatRequest) | |
220 | if err := dec(in); err != nil { | |
221 | return nil, err | |
222 | } | |
223 | if interceptor == nil { | |
224 | return srv.(AddServer).Concat(ctx, in) | |
225 | } | |
226 | info := &grpc.UnaryServerInfo{ | |
227 | Server: srv, | |
228 | FullMethod: "/pb.Add/Concat", | |
229 | } | |
230 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { | |
231 | return srv.(AddServer).Concat(ctx, req.(*ConcatRequest)) | |
232 | } | |
233 | return interceptor(ctx, in, info, handler) | |
234 | } | |
235 | ||
236 | var _Add_serviceDesc = grpc.ServiceDesc{ | |
237 | ServiceName: "pb.Add", | |
238 | HandlerType: (*AddServer)(nil), | |
239 | Methods: []grpc.MethodDesc{ | |
240 | { | |
241 | MethodName: "Sum", | |
242 | Handler: _Add_Sum_Handler, | |
243 | }, | |
244 | { | |
245 | MethodName: "Concat", | |
246 | Handler: _Add_Concat_Handler, | |
247 | }, | |
248 | }, | |
249 | Streams: []grpc.StreamDesc{}, | |
250 | Metadata: "addsvc.proto", | |
251 | } | |
252 | ||
253 | func init() { proto.RegisterFile("addsvc.proto", fileDescriptor0) } | |
254 | ||
255 | var fileDescriptor0 = []byte{ | |
256 | // 189 bytes of a gzipped FileDescriptorProto | |
257 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0x4c, 0x49, 0x29, | |
258 | 0x2e, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x48, 0x52, 0xd2, 0xe0, 0xe2, | |
259 | 0x0a, 0x2e, 0xcd, 0x0d, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0xe2, 0xe1, 0x62, 0x4c, 0x94, | |
260 | 0x60, 0x54, 0x60, 0xd4, 0x60, 0x0e, 0x62, 0x4c, 0x04, 0xf1, 0x92, 0x24, 0x98, 0x20, 0xbc, 0x24, | |
261 | 0x25, 0x2d, 0x2e, 0x0e, 0xb0, 0xca, 0x82, 0x9c, 0x4a, 0x90, 0x4c, 0x19, 0x4c, 0x5d, 0x99, 0x90, | |
262 | 0x00, 0x17, 0x73, 0x6a, 0x51, 0x11, 0x58, 0x25, 0x67, 0x10, 0x88, 0xa9, 0xa4, 0xcd, 0xc5, 0xeb, | |
263 | 0x9c, 0x9f, 0x97, 0x9c, 0x58, 0x82, 0x61, 0x30, 0x27, 0x8a, 0xc1, 0x9c, 0x20, 0x83, 0x75, 0xb9, | |
264 | 0xb8, 0x61, 0x8a, 0x51, 0xcc, 0xe6, 0xc4, 0x6a, 0xb6, 0x51, 0x0c, 0x17, 0xb3, 0x63, 0x4a, 0x8a, | |
265 | 0x90, 0x2a, 0x17, 0x73, 0x70, 0x69, 0xae, 0x10, 0x9f, 0x5e, 0x41, 0x92, 0x1e, 0xc2, 0x07, 0x52, | |
266 | 0x3c, 0x70, 0x7e, 0x41, 0x4e, 0xa5, 0x12, 0x83, 0x90, 0x1e, 0x17, 0x1b, 0xc4, 0x70, 0x21, 0x41, | |
267 | 0x90, 0x0c, 0x8a, 0xab, 0xa4, 0xf8, 0x91, 0x85, 0xc0, 0xea, 0x93, 0xd8, 0xc0, 0x41, 0x63, 0x0c, | |
268 | 0x08, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x37, 0x81, 0x99, 0x2a, 0x01, 0x00, 0x00, | |
269 | } |
0 | syntax = "proto3"; | |
1 | ||
2 | package pb; | |
3 | ||
4 | // The Add service definition. | |
5 | service Add { | |
6 | // Sums two integers. | |
7 | rpc Sum (SumRequest) returns (SumReply) {} | |
8 | ||
9 | // Concatenates two strings | |
10 | rpc Concat (ConcatRequest) returns (ConcatReply) {} | |
11 | } | |
12 | ||
13 | // The sum request contains two parameters. | |
14 | message SumRequest { | |
15 | int64 a = 1; | |
16 | int64 b = 2; | |
17 | } | |
18 | ||
19 | // The sum response contains the result of the calculation. | |
20 | message SumReply { | |
21 | int64 v = 1; | |
22 | string err = 2; | |
23 | } | |
24 | ||
25 | // The Concat request contains two parameters. | |
26 | message ConcatRequest { | |
27 | string a = 1; | |
28 | string b = 2; | |
29 | } | |
30 | ||
31 | // The Concat response contains the result of the concatenation. | |
32 | message ConcatReply { | |
33 | string v = 1; | |
34 | string err = 2; | |
35 | } |
0 | #!/usr/bin/env sh | |
1 | ||
2 | # Install proto3 from source | |
3 | # brew install autoconf automake libtool | |
4 | # git clone https://github.com/google/protobuf | |
5 | # ./autogen.sh ; ./configure ; make ; make install | |
6 | # | |
7 | # Update protoc Go bindings via | |
8 | # go get -u github.com/golang/protobuf/{proto,protoc-gen-go} | |
9 | # | |
10 | # See also | |
11 | # https://github.com/grpc/grpc-go/tree/master/examples | |
12 | ||
13 | protoc addsvc.proto --go_out=plugins=grpc:. |
0 | package addendpoint | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "fmt" | |
5 | "time" | |
6 | ||
7 | "github.com/go-kit/kit/endpoint" | |
8 | "github.com/go-kit/kit/log" | |
9 | "github.com/go-kit/kit/metrics" | |
10 | ) | |
11 | ||
12 | // InstrumentingMiddleware returns an endpoint middleware that records | |
13 | // the duration of each invocation to the passed histogram. The middleware adds | |
14 | // a single field: "success", which is "true" if no error is returned, and | |
15 | // "false" otherwise. | |
16 | func InstrumentingMiddleware(duration metrics.Histogram) endpoint.Middleware { | |
17 | return func(next endpoint.Endpoint) endpoint.Endpoint { | |
18 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { | |
19 | ||
20 | defer func(begin time.Time) { | |
21 | duration.With("success", fmt.Sprint(err == nil)).Observe(time.Since(begin).Seconds()) | |
22 | }(time.Now()) | |
23 | return next(ctx, request) | |
24 | ||
25 | } | |
26 | } | |
27 | } | |
28 | ||
29 | // LoggingMiddleware returns an endpoint middleware that logs the | |
30 | // duration of each invocation, and the resulting error, if any. | |
31 | func LoggingMiddleware(logger log.Logger) endpoint.Middleware { | |
32 | return func(next endpoint.Endpoint) endpoint.Endpoint { | |
33 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { | |
34 | ||
35 | defer func(begin time.Time) { | |
36 | logger.Log("transport_error", err, "took", time.Since(begin)) | |
37 | }(time.Now()) | |
38 | return next(ctx, request) | |
39 | ||
40 | } | |
41 | } | |
42 | } |
0 | package addendpoint | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | ||
5 | rl "github.com/juju/ratelimit" | |
6 | stdopentracing "github.com/opentracing/opentracing-go" | |
7 | "github.com/sony/gobreaker" | |
8 | ||
9 | "github.com/go-kit/kit/circuitbreaker" | |
10 | "github.com/go-kit/kit/endpoint" | |
11 | "github.com/go-kit/kit/log" | |
12 | "github.com/go-kit/kit/metrics" | |
13 | "github.com/go-kit/kit/ratelimit" | |
14 | "github.com/go-kit/kit/tracing/opentracing" | |
15 | ||
16 | "github.com/go-kit/kit/examples/addsvc/pkg/addservice" | |
17 | ) | |
18 | ||
19 | // Set collects all of the endpoints that compose an add service. It's meant to | |
20 | // be used as a helper struct, to collect all of the endpoints into a single | |
21 | // parameter. | |
22 | type Set struct { | |
23 | SumEndpoint endpoint.Endpoint | |
24 | ConcatEndpoint endpoint.Endpoint | |
25 | } | |
26 | ||
27 | // New returns a Set that wraps the provided server, and wires in all of the | |
28 | // expected endpoint middlewares via the various parameters. | |
29 | func New(svc addservice.Service, logger log.Logger, duration metrics.Histogram, trace stdopentracing.Tracer) Set { | |
30 | var sumEndpoint endpoint.Endpoint | |
31 | { | |
32 | sumEndpoint = MakeSumEndpoint(svc) | |
33 | sumEndpoint = ratelimit.NewTokenBucketLimiter(rl.NewBucketWithRate(1, 1))(sumEndpoint) | |
34 | sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(sumEndpoint) | |
35 | sumEndpoint = opentracing.TraceServer(trace, "Sum")(sumEndpoint) | |
36 | sumEndpoint = LoggingMiddleware(log.With(logger, "method", "Sum"))(sumEndpoint) | |
37 | sumEndpoint = InstrumentingMiddleware(duration.With("method", "Sum"))(sumEndpoint) | |
38 | } | |
39 | var concatEndpoint endpoint.Endpoint | |
40 | { | |
41 | concatEndpoint = MakeConcatEndpoint(svc) | |
42 | concatEndpoint = ratelimit.NewTokenBucketLimiter(rl.NewBucketWithRate(100, 100))(concatEndpoint) | |
43 | concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(concatEndpoint) | |
44 | concatEndpoint = opentracing.TraceServer(trace, "Concat")(concatEndpoint) | |
45 | concatEndpoint = LoggingMiddleware(log.With(logger, "method", "Concat"))(concatEndpoint) | |
46 | concatEndpoint = InstrumentingMiddleware(duration.With("method", "Concat"))(concatEndpoint) | |
47 | } | |
48 | return Set{ | |
49 | SumEndpoint: sumEndpoint, | |
50 | ConcatEndpoint: concatEndpoint, | |
51 | } | |
52 | } | |
53 | ||
54 | // Sum implements the service interface, so Set may be used as a service. | |
55 | // This is primarily useful in the context of a client library. | |
56 | func (s Set) Sum(ctx context.Context, a, b int) (int, error) { | |
57 | resp, err := s.SumEndpoint(ctx, SumRequest{A: a, B: b}) | |
58 | if err != nil { | |
59 | return 0, err | |
60 | } | |
61 | response := resp.(SumResponse) | |
62 | return response.V, response.Err | |
63 | } | |
64 | ||
65 | // Concat implements the service interface, so Set may be used as a | |
66 | // service. This is primarily useful in the context of a client library. | |
67 | func (s Set) Concat(ctx context.Context, a, b string) (string, error) { | |
68 | resp, err := s.ConcatEndpoint(ctx, ConcatRequest{A: a, B: b}) | |
69 | if err != nil { | |
70 | return "", err | |
71 | } | |
72 | response := resp.(ConcatResponse) | |
73 | return response.V, response.Err | |
74 | } | |
75 | ||
76 | // MakeSumEndpoint constructs a Sum endpoint wrapping the service. | |
77 | func MakeSumEndpoint(s addservice.Service) endpoint.Endpoint { | |
78 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { | |
79 | req := request.(SumRequest) | |
80 | v, err := s.Sum(ctx, req.A, req.B) | |
81 | return SumResponse{V: v, Err: err}, nil | |
82 | } | |
83 | } | |
84 | ||
85 | // MakeConcatEndpoint constructs a Concat endpoint wrapping the service. | |
86 | func MakeConcatEndpoint(s addservice.Service) endpoint.Endpoint { | |
87 | return func(ctx context.Context, request interface{}) (response interface{}, err error) { | |
88 | req := request.(ConcatRequest) | |
89 | v, err := s.Concat(ctx, req.A, req.B) | |
90 | return ConcatResponse{V: v, Err: err}, nil | |
91 | } | |
92 | } | |
93 | ||
94 | // Failer is an interface that should be implemented by response types. | |
95 | // Response encoders can check if responses are Failer, and if so if they've | |
96 | // failed, and if so encode them using a separate write path based on the error. | |
97 | type Failer interface { | |
98 | Failed() error | |
99 | } | |
100 | ||
101 | // SumRequest collects the request parameters for the Sum method. | |
102 | type SumRequest struct { | |
103 | A, B int | |
104 | } | |
105 | ||
106 | // SumResponse collects the response values for the Sum method. | |
107 | type SumResponse struct { | |
108 | V int `json:"v"` | |
109 | Err error `json:"-"` // should be intercepted by Failed/errorEncoder | |
110 | } | |
111 | ||
112 | // Failed implements Failer. | |
113 | func (r SumResponse) Failed() error { return r.Err } | |
114 | ||
115 | // ConcatRequest collects the request parameters for the Concat method. | |
116 | type ConcatRequest struct { | |
117 | A, B string | |
118 | } | |
119 | ||
120 | // ConcatResponse collects the response values for the Concat method. | |
121 | type ConcatResponse struct { | |
122 | V string `json:"v"` | |
123 | Err error `json:"-"` | |
124 | } | |
125 | ||
126 | // Failed implements Failer. | |
127 | func (r ConcatResponse) Failed() error { return r.Err } |
0 | package addservice | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | ||
5 | "github.com/go-kit/kit/log" | |
6 | "github.com/go-kit/kit/metrics" | |
7 | ) | |
8 | ||
9 | // Middleware describes a service (as opposed to endpoint) middleware. | |
10 | type Middleware func(Service) Service | |
11 | ||
12 | // LoggingMiddleware takes a logger as a dependency | |
13 | // and returns a ServiceMiddleware. | |
14 | func LoggingMiddleware(logger log.Logger) Middleware { | |
15 | return func(next Service) Service { | |
16 | return loggingMiddleware{logger, next} | |
17 | } | |
18 | } | |
19 | ||
20 | type loggingMiddleware struct { | |
21 | logger log.Logger | |
22 | next Service | |
23 | } | |
24 | ||
25 | func (mw loggingMiddleware) Sum(ctx context.Context, a, b int) (v int, err error) { | |
26 | defer func() { | |
27 | mw.logger.Log("method", "Sum", "a", a, "b", b, "v", v, "err", err) | |
28 | }() | |
29 | return mw.next.Sum(ctx, a, b) | |
30 | } | |
31 | ||
32 | func (mw loggingMiddleware) Concat(ctx context.Context, a, b string) (v string, err error) { | |
33 | defer func() { | |
34 | mw.logger.Log("method", "Concat", "a", a, "b", b, "v", v, "err", err) | |
35 | }() | |
36 | return mw.next.Concat(ctx, a, b) | |
37 | } | |
38 | ||
39 | // InstrumentingMiddleware returns a service middleware that instruments | |
40 | // the number of integers summed and characters concatenated over the lifetime of | |
41 | // the service. | |
42 | func InstrumentingMiddleware(ints, chars metrics.Counter) Middleware { | |
43 | return func(next Service) Service { | |
44 | return instrumentingMiddleware{ | |
45 | ints: ints, | |
46 | chars: chars, | |
47 | next: next, | |
48 | } | |
49 | } | |
50 | } | |
51 | ||
52 | type instrumentingMiddleware struct { | |
53 | ints metrics.Counter | |
54 | chars metrics.Counter | |
55 | next Service | |
56 | } | |
57 | ||
58 | func (mw instrumentingMiddleware) Sum(ctx context.Context, a, b int) (int, error) { | |
59 | v, err := mw.next.Sum(ctx, a, b) | |
60 | mw.ints.Add(float64(v)) | |
61 | return v, err | |
62 | } | |
63 | ||
64 | func (mw instrumentingMiddleware) Concat(ctx context.Context, a, b string) (string, error) { | |
65 | v, err := mw.next.Concat(ctx, a, b) | |
66 | mw.chars.Add(float64(len(v))) | |
67 | return v, err | |
68 | } |
0 | package addservice | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | ||
6 | "github.com/go-kit/kit/log" | |
7 | "github.com/go-kit/kit/metrics" | |
8 | ) | |
9 | ||
10 | // Service describes a service that adds things together. | |
11 | type Service interface { | |
12 | Sum(ctx context.Context, a, b int) (int, error) | |
13 | Concat(ctx context.Context, a, b string) (string, error) | |
14 | } | |
15 | ||
16 | // New returns a basic Service with all of the expected middlewares wired in. | |
17 | func New(logger log.Logger, ints, chars metrics.Counter) Service { | |
18 | var svc Service | |
19 | { | |
20 | svc = NewBasicService() | |
21 | svc = LoggingMiddleware(logger)(svc) | |
22 | svc = InstrumentingMiddleware(ints, chars)(svc) | |
23 | } | |
24 | return svc | |
25 | } | |
26 | ||
27 | var ( | |
28 | // ErrTwoZeroes is an arbitrary business rule for the Add method. | |
29 | ErrTwoZeroes = errors.New("can't sum two zeroes") | |
30 | ||
31 | // ErrIntOverflow protects the Add method. We've decided that this error | |
32 | // indicates a misbehaving service and should count against e.g. circuit | |
33 | // breakers. So, we return it directly in endpoints, to illustrate the | |
34 | // difference. In a real service, this probably wouldn't be the case. | |
35 | ErrIntOverflow = errors.New("integer overflow") | |
36 | ||
37 | // ErrMaxSizeExceeded protects the Concat method. | |
38 | ErrMaxSizeExceeded = errors.New("result exceeds maximum size") | |
39 | ) | |
40 | ||
41 | // NewBasicService returns a naïve, stateless implementation of Service. | |
42 | func NewBasicService() Service { | |
43 | return basicService{} | |
44 | } | |
45 | ||
46 | type basicService struct{} | |
47 | ||
48 | const ( | |
49 | intMax = 1<<31 - 1 | |
50 | intMin = -(intMax + 1) | |
51 | maxLen = 10 | |
52 | ) | |
53 | ||
54 | func (s basicService) Sum(_ context.Context, a, b int) (int, error) { | |
55 | if a == 0 && b == 0 { | |
56 | return 0, ErrTwoZeroes | |
57 | } | |
58 | if (b > 0 && a > (intMax-b)) || (b < 0 && a < (intMin-b)) { | |
59 | return 0, ErrIntOverflow | |
60 | } | |
61 | return a + b, nil | |
62 | } | |
63 | ||
64 | // Concat implements Service. | |
65 | func (s basicService) Concat(_ context.Context, a, b string) (string, error) { | |
66 | if len(a)+len(b) > maxLen { | |
67 | return "", ErrMaxSizeExceeded | |
68 | } | |
69 | return a + b, nil | |
70 | } |
0 | package addtransport | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "errors" | |
5 | "time" | |
6 | ||
7 | "google.golang.org/grpc" | |
8 | ||
9 | jujuratelimit "github.com/juju/ratelimit" | |
10 | stdopentracing "github.com/opentracing/opentracing-go" | |
11 | "github.com/sony/gobreaker" | |
12 | oldcontext "golang.org/x/net/context" | |
13 | ||
14 | "github.com/go-kit/kit/circuitbreaker" | |
15 | "github.com/go-kit/kit/endpoint" | |
16 | "github.com/go-kit/kit/log" | |
17 | "github.com/go-kit/kit/ratelimit" | |
18 | "github.com/go-kit/kit/tracing/opentracing" | |
19 | grpctransport "github.com/go-kit/kit/transport/grpc" | |
20 | ||
21 | "github.com/go-kit/kit/examples/addsvc/pb" | |
22 | "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" | |
23 | "github.com/go-kit/kit/examples/addsvc/pkg/addservice" | |
24 | ) | |
25 | ||
26 | type grpcServer struct { | |
27 | sum grpctransport.Handler | |
28 | concat grpctransport.Handler | |
29 | } | |
30 | ||
31 | // NewGRPCServer makes a set of endpoints available as a gRPC AddServer. | |
32 | func NewGRPCServer(endpoints addendpoint.Set, tracer stdopentracing.Tracer, logger log.Logger) pb.AddServer { | |
33 | options := []grpctransport.ServerOption{ | |
34 | grpctransport.ServerErrorLogger(logger), | |
35 | } | |
36 | return &grpcServer{ | |
37 | sum: grpctransport.NewServer( | |
38 | endpoints.SumEndpoint, | |
39 | decodeGRPCSumRequest, | |
40 | encodeGRPCSumResponse, | |
41 | append(options, grpctransport.ServerBefore(opentracing.GRPCToContext(tracer, "Sum", logger)))..., | |
42 | ), | |
43 | concat: grpctransport.NewServer( | |
44 | endpoints.ConcatEndpoint, | |
45 | decodeGRPCConcatRequest, | |
46 | encodeGRPCConcatResponse, | |
47 | append(options, grpctransport.ServerBefore(opentracing.GRPCToContext(tracer, "Concat", logger)))..., | |
48 | ), | |
49 | } | |
50 | } | |
51 | ||
52 | func (s *grpcServer) Sum(ctx oldcontext.Context, req *pb.SumRequest) (*pb.SumReply, error) { | |
53 | _, rep, err := s.sum.ServeGRPC(ctx, req) | |
54 | if err != nil { | |
55 | return nil, err | |
56 | } | |
57 | return rep.(*pb.SumReply), nil | |
58 | } | |
59 | ||
60 | func (s *grpcServer) Concat(ctx oldcontext.Context, req *pb.ConcatRequest) (*pb.ConcatReply, error) { | |
61 | _, rep, err := s.concat.ServeGRPC(ctx, req) | |
62 | if err != nil { | |
63 | return nil, err | |
64 | } | |
65 | return rep.(*pb.ConcatReply), nil | |
66 | } | |
67 | ||
68 | // NewGRPCClient returns an AddService backed by a gRPC server at the other end | |
69 | // of the conn. The caller is responsible for constructing the conn, and | |
70 | // eventually closing the underlying transport. We bake-in certain middlewares, | |
71 | // implementing the client library pattern. | |
72 | func NewGRPCClient(conn *grpc.ClientConn, tracer stdopentracing.Tracer, logger log.Logger) addservice.Service { | |
73 | // We construct a single ratelimiter middleware, to limit the total outgoing | |
74 | // QPS from this client to all methods on the remote instance. We also | |
75 | // construct per-endpoint circuitbreaker middlewares to demonstrate how | |
76 | // that's done, although they could easily be combined into a single breaker | |
77 | // for the entire remote instance, too. | |
78 | limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100)) | |
79 | ||
80 | // Each individual endpoint is an http/transport.Client (which implements | |
81 | // endpoint.Endpoint) that gets wrapped with various middlewares. If you | |
82 | // made your own client library, you'd do this work there, so your server | |
83 | // could rely on a consistent set of client behavior. | |
84 | var sumEndpoint endpoint.Endpoint | |
85 | { | |
86 | sumEndpoint = grpctransport.NewClient( | |
87 | conn, | |
88 | "pb.Add", | |
89 | "Sum", | |
90 | encodeGRPCSumRequest, | |
91 | decodeGRPCSumResponse, | |
92 | pb.SumReply{}, | |
93 | grpctransport.ClientBefore(opentracing.ContextToGRPC(tracer, logger)), | |
94 | ).Endpoint() | |
95 | sumEndpoint = opentracing.TraceClient(tracer, "Sum")(sumEndpoint) | |
96 | sumEndpoint = limiter(sumEndpoint) | |
97 | sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ | |
98 | Name: "Sum", | |
99 | Timeout: 30 * time.Second, | |
100 | }))(sumEndpoint) | |
101 | } | |
102 | ||
103 | // The Concat endpoint is the same thing, with slightly different | |
104 | // middlewares to demonstrate how to specialize per-endpoint. | |
105 | var concatEndpoint endpoint.Endpoint | |
106 | { | |
107 | concatEndpoint = grpctransport.NewClient( | |
108 | conn, | |
109 | "pb.Add", | |
110 | "Concat", | |
111 | encodeGRPCConcatRequest, | |
112 | decodeGRPCConcatResponse, | |
113 | pb.ConcatReply{}, | |
114 | grpctransport.ClientBefore(opentracing.ContextToGRPC(tracer, logger)), | |
115 | ).Endpoint() | |
116 | concatEndpoint = opentracing.TraceClient(tracer, "Concat")(concatEndpoint) | |
117 | concatEndpoint = limiter(concatEndpoint) | |
118 | concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ | |
119 | Name: "Concat", | |
120 | Timeout: 10 * time.Second, | |
121 | }))(concatEndpoint) | |
122 | } | |
123 | ||
124 | // Returning the endpoint.Set as a service.Service relies on the | |
125 | // endpoint.Set implementing the Service methods. That's just a simple bit | |
126 | // of glue code. | |
127 | return addendpoint.Set{ | |
128 | SumEndpoint: sumEndpoint, | |
129 | ConcatEndpoint: concatEndpoint, | |
130 | } | |
131 | } | |
132 | ||
133 | // decodeGRPCSumRequest is a transport/grpc.DecodeRequestFunc that converts a | |
134 | // gRPC sum request to a user-domain sum request. Primarily useful in a server. | |
135 | func decodeGRPCSumRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { | |
136 | req := grpcReq.(*pb.SumRequest) | |
137 | return addendpoint.SumRequest{A: int(req.A), B: int(req.B)}, nil | |
138 | } | |
139 | ||
140 | // decodeGRPCConcatRequest is a transport/grpc.DecodeRequestFunc that converts a | |
141 | // gRPC concat request to a user-domain concat request. Primarily useful in a | |
142 | // server. | |
143 | func decodeGRPCConcatRequest(_ context.Context, grpcReq interface{}) (interface{}, error) { | |
144 | req := grpcReq.(*pb.ConcatRequest) | |
145 | return addendpoint.ConcatRequest{A: req.A, B: req.B}, nil | |
146 | } | |
147 | ||
148 | // decodeGRPCSumResponse is a transport/grpc.DecodeResponseFunc that converts a | |
149 | // gRPC sum reply to a user-domain sum response. Primarily useful in a client. | |
150 | func decodeGRPCSumResponse(_ context.Context, grpcReply interface{}) (interface{}, error) { | |
151 | reply := grpcReply.(*pb.SumReply) | |
152 | return addendpoint.SumResponse{V: int(reply.V), Err: str2err(reply.Err)}, nil | |
153 | } | |
154 | ||
155 | // decodeGRPCConcatResponse is a transport/grpc.DecodeResponseFunc that converts | |
156 | // a gRPC concat reply to a user-domain concat response. Primarily useful in a | |
157 | // client. | |
158 | func decodeGRPCConcatResponse(_ context.Context, grpcReply interface{}) (interface{}, error) { | |
159 | reply := grpcReply.(*pb.ConcatReply) | |
160 | return addendpoint.ConcatResponse{V: reply.V, Err: str2err(reply.Err)}, nil | |
161 | } | |
162 | ||
163 | // encodeGRPCSumResponse is a transport/grpc.EncodeResponseFunc that converts a | |
164 | // user-domain sum response to a gRPC sum reply. Primarily useful in a server. | |
165 | func encodeGRPCSumResponse(_ context.Context, response interface{}) (interface{}, error) { | |
166 | resp := response.(addendpoint.SumResponse) | |
167 | return &pb.SumReply{V: int64(resp.V), Err: err2str(resp.Err)}, nil | |
168 | } | |
169 | ||
170 | // encodeGRPCConcatResponse is a transport/grpc.EncodeResponseFunc that converts | |
171 | // a user-domain concat response to a gRPC concat reply. Primarily useful in a | |
172 | // server. | |
173 | func encodeGRPCConcatResponse(_ context.Context, response interface{}) (interface{}, error) { | |
174 | resp := response.(addendpoint.ConcatResponse) | |
175 | return &pb.ConcatReply{V: resp.V, Err: err2str(resp.Err)}, nil | |
176 | } | |
177 | ||
178 | // encodeGRPCSumRequest is a transport/grpc.EncodeRequestFunc that converts a | |
179 | // user-domain sum request to a gRPC sum request. Primarily useful in a client. | |
180 | func encodeGRPCSumRequest(_ context.Context, request interface{}) (interface{}, error) { | |
181 | req := request.(addendpoint.SumRequest) | |
182 | return &pb.SumRequest{A: int64(req.A), B: int64(req.B)}, nil | |
183 | } | |
184 | ||
185 | // encodeGRPCConcatRequest is a transport/grpc.EncodeRequestFunc that converts a | |
186 | // user-domain concat request to a gRPC concat request. Primarily useful in a | |
187 | // client. | |
188 | func encodeGRPCConcatRequest(_ context.Context, request interface{}) (interface{}, error) { | |
189 | req := request.(addendpoint.ConcatRequest) | |
190 | return &pb.ConcatRequest{A: req.A, B: req.B}, nil | |
191 | } | |
192 | ||
193 | // These annoying helper functions are required to translate Go error types to | |
194 | // and from strings, which is the type we use in our IDLs to represent errors. | |
195 | // There is special casing to treat empty strings as nil errors. | |
196 | ||
197 | func str2err(s string) error { | |
198 | if s == "" { | |
199 | return nil | |
200 | } | |
201 | return errors.New(s) | |
202 | } | |
203 | ||
204 | func err2str(err error) string { | |
205 | if err == nil { | |
206 | return "" | |
207 | } | |
208 | return err.Error() | |
209 | } |
0 | package addtransport | |
1 | ||
2 | import ( | |
3 | "bytes" | |
4 | "context" | |
5 | "encoding/json" | |
6 | "errors" | |
7 | "io/ioutil" | |
8 | "net/http" | |
9 | "net/url" | |
10 | "strings" | |
11 | "time" | |
12 | ||
13 | jujuratelimit "github.com/juju/ratelimit" | |
14 | stdopentracing "github.com/opentracing/opentracing-go" | |
15 | "github.com/sony/gobreaker" | |
16 | ||
17 | "github.com/go-kit/kit/circuitbreaker" | |
18 | "github.com/go-kit/kit/endpoint" | |
19 | "github.com/go-kit/kit/log" | |
20 | "github.com/go-kit/kit/ratelimit" | |
21 | "github.com/go-kit/kit/tracing/opentracing" | |
22 | httptransport "github.com/go-kit/kit/transport/http" | |
23 | ||
24 | "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" | |
25 | "github.com/go-kit/kit/examples/addsvc/pkg/addservice" | |
26 | ) | |
27 | ||
28 | // NewHTTPHandler returns an HTTP handler that makes a set of endpoints | |
29 | // available on predefined paths. | |
30 | func NewHTTPHandler(endpoints addendpoint.Set, tracer stdopentracing.Tracer, logger log.Logger) http.Handler { | |
31 | options := []httptransport.ServerOption{ | |
32 | httptransport.ServerErrorEncoder(errorEncoder), | |
33 | httptransport.ServerErrorLogger(logger), | |
34 | } | |
35 | m := http.NewServeMux() | |
36 | m.Handle("/sum", httptransport.NewServer( | |
37 | endpoints.SumEndpoint, | |
38 | decodeHTTPSumRequest, | |
39 | encodeHTTPGenericResponse, | |
40 | append(options, httptransport.ServerBefore(opentracing.HTTPToContext(tracer, "Sum", logger)))..., | |
41 | )) | |
42 | m.Handle("/concat", httptransport.NewServer( | |
43 | endpoints.ConcatEndpoint, | |
44 | decodeHTTPConcatRequest, | |
45 | encodeHTTPGenericResponse, | |
46 | append(options, httptransport.ServerBefore(opentracing.HTTPToContext(tracer, "Concat", logger)))..., | |
47 | )) | |
48 | return m | |
49 | } | |
50 | ||
51 | // NewHTTPClient returns an AddService backed by an HTTP server living at the | |
52 | // remote instance. We expect instance to come from a service discovery system, | |
53 | // so likely of the form "host:port". We bake-in certain middlewares, | |
54 | // implementing the client library pattern. | |
55 | func NewHTTPClient(instance string, tracer stdopentracing.Tracer, logger log.Logger) (addservice.Service, error) { | |
56 | // Quickly sanitize the instance string. | |
57 | if !strings.HasPrefix(instance, "http") { | |
58 | instance = "http://" + instance | |
59 | } | |
60 | u, err := url.Parse(instance) | |
61 | if err != nil { | |
62 | return nil, err | |
63 | } | |
64 | ||
65 | // We construct a single ratelimiter middleware, to limit the total outgoing | |
66 | // QPS from this client to all methods on the remote instance. We also | |
67 | // construct per-endpoint circuitbreaker middlewares to demonstrate how | |
68 | // that's done, although they could easily be combined into a single breaker | |
69 | // for the entire remote instance, too. | |
70 | limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100)) | |
71 | ||
72 | // Each individual endpoint is an http/transport.Client (which implements | |
73 | // endpoint.Endpoint) that gets wrapped with various middlewares. If you | |
74 | // made your own client library, you'd do this work there, so your server | |
75 | // could rely on a consistent set of client behavior. | |
76 | var sumEndpoint endpoint.Endpoint | |
77 | { | |
78 | sumEndpoint = httptransport.NewClient( | |
79 | "POST", | |
80 | copyURL(u, "/sum"), | |
81 | encodeHTTPGenericRequest, | |
82 | decodeHTTPSumResponse, | |
83 | httptransport.ClientBefore(opentracing.ContextToHTTP(tracer, logger)), | |
84 | ).Endpoint() | |
85 | sumEndpoint = opentracing.TraceClient(tracer, "Sum")(sumEndpoint) | |
86 | sumEndpoint = limiter(sumEndpoint) | |
87 | sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ | |
88 | Name: "Sum", | |
89 | Timeout: 30 * time.Second, | |
90 | }))(sumEndpoint) | |
91 | } | |
92 | ||
93 | // The Concat endpoint is the same thing, with slightly different | |
94 | // middlewares to demonstrate how to specialize per-endpoint. | |
95 | var concatEndpoint endpoint.Endpoint | |
96 | { | |
97 | concatEndpoint = httptransport.NewClient( | |
98 | "POST", | |
99 | copyURL(u, "/concat"), | |
100 | encodeHTTPGenericRequest, | |
101 | decodeHTTPConcatResponse, | |
102 | httptransport.ClientBefore(opentracing.ContextToHTTP(tracer, logger)), | |
103 | ).Endpoint() | |
104 | concatEndpoint = opentracing.TraceClient(tracer, "Concat")(concatEndpoint) | |
105 | concatEndpoint = limiter(concatEndpoint) | |
106 | concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ | |
107 | Name: "Concat", | |
108 | Timeout: 10 * time.Second, | |
109 | }))(concatEndpoint) | |
110 | } | |
111 | ||
112 | // Returning the endpoint.Set as a service.Service relies on the | |
113 | // endpoint.Set implementing the Service methods. That's just a simple bit | |
114 | // of glue code. | |
115 | return addendpoint.Set{ | |
116 | SumEndpoint: sumEndpoint, | |
117 | ConcatEndpoint: concatEndpoint, | |
118 | }, nil | |
119 | } | |
120 | ||
121 | func copyURL(base *url.URL, path string) *url.URL { | |
122 | next := *base | |
123 | next.Path = path | |
124 | return &next | |
125 | } | |
126 | ||
127 | func errorEncoder(_ context.Context, err error, w http.ResponseWriter) { | |
128 | w.WriteHeader(err2code(err)) | |
129 | json.NewEncoder(w).Encode(errorWrapper{Error: err.Error()}) | |
130 | } | |
131 | ||
132 | func err2code(err error) int { | |
133 | switch err { | |
134 | case addservice.ErrTwoZeroes, addservice.ErrMaxSizeExceeded, addservice.ErrIntOverflow: | |
135 | return http.StatusBadRequest | |
136 | } | |
137 | return http.StatusInternalServerError | |
138 | } | |
139 | ||
140 | func errorDecoder(r *http.Response) error { | |
141 | var w errorWrapper | |
142 | if err := json.NewDecoder(r.Body).Decode(&w); err != nil { | |
143 | return err | |
144 | } | |
145 | return errors.New(w.Error) | |
146 | } | |
147 | ||
148 | type errorWrapper struct { | |
149 | Error string `json:"error"` | |
150 | } | |
151 | ||
152 | // decodeHTTPSumRequest is a transport/http.DecodeRequestFunc that decodes a | |
153 | // JSON-encoded sum request from the HTTP request body. Primarily useful in a | |
154 | // server. | |
155 | func decodeHTTPSumRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
156 | var req addendpoint.SumRequest | |
157 | err := json.NewDecoder(r.Body).Decode(&req) | |
158 | return req, err | |
159 | } | |
160 | ||
161 | // decodeHTTPConcatRequest is a transport/http.DecodeRequestFunc that decodes a | |
162 | // JSON-encoded concat request from the HTTP request body. Primarily useful in a | |
163 | // server. | |
164 | func decodeHTTPConcatRequest(_ context.Context, r *http.Request) (interface{}, error) { | |
165 | var req addendpoint.ConcatRequest | |
166 | err := json.NewDecoder(r.Body).Decode(&req) | |
167 | return req, err | |
168 | } | |
169 | ||
170 | // decodeHTTPSumResponse is a transport/http.DecodeResponseFunc that decodes a | |
171 | // JSON-encoded sum response from the HTTP response body. If the response has a | |
172 | // non-200 status code, we will interpret that as an error and attempt to decode | |
173 | // the specific error message from the response body. Primarily useful in a | |
174 | // client. | |
175 | func decodeHTTPSumResponse(_ context.Context, r *http.Response) (interface{}, error) { | |
176 | if r.StatusCode != http.StatusOK { | |
177 | return nil, errors.New(r.Status) | |
178 | } | |
179 | var resp addendpoint.SumResponse | |
180 | err := json.NewDecoder(r.Body).Decode(&resp) | |
181 | return resp, err | |
182 | } | |
183 | ||
184 | // decodeHTTPConcatResponse is a transport/http.DecodeResponseFunc that decodes | |
185 | // a JSON-encoded concat response from the HTTP response body. If the response | |
186 | // has a non-200 status code, we will interpret that as an error and attempt to | |
187 | // decode the specific error message from the response body. Primarily useful in | |
188 | // a client. | |
189 | func decodeHTTPConcatResponse(_ context.Context, r *http.Response) (interface{}, error) { | |
190 | if r.StatusCode != http.StatusOK { | |
191 | return nil, errors.New(r.Status) | |
192 | } | |
193 | var resp addendpoint.ConcatResponse | |
194 | err := json.NewDecoder(r.Body).Decode(&resp) | |
195 | return resp, err | |
196 | } | |
197 | ||
198 | // encodeHTTPGenericRequest is a transport/http.EncodeRequestFunc that | |
199 | // JSON-encodes any request to the request body. Primarily useful in a client. | |
200 | func encodeHTTPGenericRequest(_ context.Context, r *http.Request, request interface{}) error { | |
201 | var buf bytes.Buffer | |
202 | if err := json.NewEncoder(&buf).Encode(request); err != nil { | |
203 | return err | |
204 | } | |
205 | r.Body = ioutil.NopCloser(&buf) | |
206 | return nil | |
207 | } | |
208 | ||
209 | // encodeHTTPGenericResponse is a transport/http.EncodeResponseFunc that encodes | |
210 | // the response as JSON to the response writer. Primarily useful in a server. | |
211 | func encodeHTTPGenericResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { | |
212 | if f, ok := response.(addendpoint.Failer); ok && f.Failed() != nil { | |
213 | errorEncoder(ctx, f.Failed(), w) | |
214 | return nil | |
215 | } | |
216 | w.Header().Set("Content-Type", "application/json; charset=utf-8") | |
217 | return json.NewEncoder(w).Encode(response) | |
218 | } |
0 | package addtransport | |
1 | ||
2 | import ( | |
3 | "context" | |
4 | "time" | |
5 | ||
6 | jujuratelimit "github.com/juju/ratelimit" | |
7 | "github.com/sony/gobreaker" | |
8 | ||
9 | "github.com/go-kit/kit/circuitbreaker" | |
10 | "github.com/go-kit/kit/endpoint" | |
11 | "github.com/go-kit/kit/ratelimit" | |
12 | ||
13 | "github.com/go-kit/kit/examples/addsvc/pkg/addendpoint" | |
14 | "github.com/go-kit/kit/examples/addsvc/pkg/addservice" | |
15 | addthrift "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" | |
16 | ) | |
17 | ||
18 | type thriftServer struct { | |
19 | ctx context.Context | |
20 | endpoints addendpoint.Set | |
21 | } | |
22 | ||
23 | // NewThriftServer makes a set of endpoints available as a Thrift service. | |
24 | func NewThriftServer(endpoints addendpoint.Set) addthrift.AddService { | |
25 | return &thriftServer{ | |
26 | endpoints: endpoints, | |
27 | } | |
28 | } | |
29 | ||
30 | func (s *thriftServer) Sum(ctx context.Context, a int64, b int64) (*addthrift.SumReply, error) { | |
31 | request := addendpoint.SumRequest{A: int(a), B: int(b)} | |
32 | response, err := s.endpoints.SumEndpoint(ctx, request) | |
33 | if err != nil { | |
34 | return nil, err | |
35 | } | |
36 | resp := response.(addendpoint.SumResponse) | |
37 | return &addthrift.SumReply{Value: int64(resp.V), Err: err2str(resp.Err)}, nil | |
38 | } | |
39 | ||
40 | func (s *thriftServer) Concat(ctx context.Context, a string, b string) (*addthrift.ConcatReply, error) { | |
41 | request := addendpoint.ConcatRequest{A: a, B: b} | |
42 | response, err := s.endpoints.ConcatEndpoint(ctx, request) | |
43 | if err != nil { | |
44 | return nil, err | |
45 | } | |
46 | resp := response.(addendpoint.ConcatResponse) | |
47 | return &addthrift.ConcatReply{Value: resp.V, Err: err2str(resp.Err)}, nil | |
48 | } | |
49 | ||
50 | // NewThriftClient returns an AddService backed by a Thrift server described by | |
51 | // the provided client. The caller is responsible for constructing the client, | |
52 | // and eventually closing the underlying transport. We bake-in certain middlewares, | |
53 | // implementing the client library pattern. | |
54 | func NewThriftClient(client *addthrift.AddServiceClient) addservice.Service { | |
55 | // We construct a single ratelimiter middleware, to limit the total outgoing | |
56 | // QPS from this client to all methods on the remote instance. We also | |
57 | // construct per-endpoint circuitbreaker middlewares to demonstrate how | |
58 | // that's done, although they could easily be combined into a single breaker | |
59 | // for the entire remote instance, too. | |
60 | limiter := ratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(100, 100)) | |
61 | ||
62 | // Each individual endpoint is an http/transport.Client (which implements | |
63 | // endpoint.Endpoint) that gets wrapped with various middlewares. If you | |
64 | // could rely on a consistent set of client behavior. | |
65 | var sumEndpoint endpoint.Endpoint | |
66 | { | |
67 | sumEndpoint = MakeThriftSumEndpoint(client) | |
68 | sumEndpoint = limiter(sumEndpoint) | |
69 | sumEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ | |
70 | Name: "Sum", | |
71 | Timeout: 30 * time.Second, | |
72 | }))(sumEndpoint) | |
73 | } | |
74 | ||
75 | // The Concat endpoint is the same thing, with slightly different | |
76 | // middlewares to demonstrate how to specialize per-endpoint. | |
77 | var concatEndpoint endpoint.Endpoint | |
78 | { | |
79 | concatEndpoint = MakeThriftConcatEndpoint(client) | |
80 | concatEndpoint = limiter(concatEndpoint) | |
81 | concatEndpoint = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{ | |
82 | Name: "Concat", | |
83 | Timeout: 10 * time.Second, | |
84 | }))(concatEndpoint) | |
85 | } | |
86 | ||
87 | // Returning the endpoint.Set as a service.Service relies on the | |
88 | // endpoint.Set implementing the Service methods. That's just a simple bit | |
89 | // of glue code. | |
90 | return addendpoint.Set{ | |
91 | SumEndpoint: sumEndpoint, | |
92 | ConcatEndpoint: concatEndpoint, | |
93 | } | |
94 | } | |
95 | ||
96 | // MakeThriftSumEndpoint returns an endpoint that invokes the passed Thrift client. | |
97 | // Useful only in clients, and only until a proper transport/thrift.Client exists. | |
98 | func MakeThriftSumEndpoint(client *addthrift.AddServiceClient) endpoint.Endpoint { | |
99 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
100 | req := request.(addendpoint.SumRequest) | |
101 | reply, err := client.Sum(ctx, int64(req.A), int64(req.B)) | |
102 | if err == addservice.ErrIntOverflow { | |
103 | return nil, err // special case; see comment on ErrIntOverflow | |
104 | } | |
105 | return addendpoint.SumResponse{V: int(reply.Value), Err: err}, nil | |
106 | } | |
107 | } | |
108 | ||
109 | // MakeThriftConcatEndpoint returns an endpoint that invokes the passed Thrift | |
110 | // client. Useful only in clients, and only until a proper | |
111 | // transport/thrift.Client exists. | |
112 | func MakeThriftConcatEndpoint(client *addthrift.AddServiceClient) endpoint.Endpoint { | |
113 | return func(ctx context.Context, request interface{}) (interface{}, error) { | |
114 | req := request.(addendpoint.ConcatRequest) | |
115 | reply, err := client.Concat(ctx, req.A, req.B) | |
116 | return addendpoint.ConcatResponse{V: reply.Value, Err: err}, nil | |
117 | } | |
118 | } |
0 | struct SumReply { | |
1 | 1: i64 value | |
2 | 2: string err | |
3 | } | |
4 | ||
5 | struct ConcatReply { | |
6 | 1: string value | |
7 | 2: string err | |
8 | } | |
9 | ||
10 | service AddService { | |
11 | SumReply Sum(1: i64 a, 2: i64 b) | |
12 | ConcatReply Concat(1: string a, 2: string b) | |
13 | } |
0 | #!/usr/bin/env sh | |
1 | ||
2 | # See also https://thrift.apache.org/tutorial/go | |
3 | ||
4 | thrift -r --gen "go:package_prefix=github.com/go-kit/kit/examples/addsvc/thrift/gen-go/,thrift_import=github.com/apache/thrift/lib/go/thrift" addsvc.thrift |
0 | // Autogenerated by Thrift Compiler (1.0.0-dev) | |
1 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING | |
2 | ||
3 | package addsvc | |
4 | ||
5 | var GoUnusedProtection__ int; | |
6 |
0 | // Autogenerated by Thrift Compiler (1.0.0-dev) | |
1 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING | |
2 | ||
3 | package main | |
4 | ||
5 | import ( | |
6 | "context" | |
7 | "flag" | |
8 | "fmt" | |
9 | "math" | |
10 | "net" | |
11 | "net/url" | |
12 | "os" | |
13 | "strconv" | |
14 | "strings" | |
15 | "github.com/apache/thrift/lib/go/thrift" | |
16 | "github.com/go-kit/kit/examples/addsvc/thrift/gen-go/addsvc" | |
17 | ) | |
18 | ||
19 | ||
20 | func Usage() { | |
21 | fmt.Fprintln(os.Stderr, "Usage of ", os.Args[0], " [-h host:port] [-u url] [-f[ramed]] function [arg1 [arg2...]]:") | |
22 | flag.PrintDefaults() | |
23 | fmt.Fprintln(os.Stderr, "\nFunctions:") | |
24 | fmt.Fprintln(os.Stderr, " SumReply Sum(i64 a, i64 b)") | |
25 | fmt.Fprintln(os.Stderr, " ConcatReply Concat(string a, string b)") | |
26 | fmt.Fprintln(os.Stderr) | |
27 | os.Exit(0) | |
28 | } | |
29 | ||
30 | func main() { | |
31 | flag.Usage = Usage | |
32 | var host string | |
33 | var port int | |
34 | var protocol string | |
35 | var urlString string | |
36 | var framed bool | |
37 | var useHttp bool | |
38 | var parsedUrl url.URL | |
39 | var trans thrift.TTransport | |
40 | _ = strconv.Atoi | |
41 | _ = math.Abs | |
42 | flag.Usage = Usage | |
43 | flag.StringVar(&host, "h", "localhost", "Specify host and port") | |
44 | flag.IntVar(&port, "p", 9090, "Specify port") | |
45 | flag.StringVar(&protocol, "P", "binary", "Specify the protocol (binary, compact, simplejson, json)") | |
46 | flag.StringVar(&urlString, "u", "", "Specify the url") | |
47 | flag.BoolVar(&framed, "framed", false, "Use framed transport") | |
48 | flag.BoolVar(&useHttp, "http", false, "Use http") | |
49 | flag.Parse() | |
50 | ||
51 | if len(urlString) > 0 { | |
52 | parsedUrl, err := url.Parse(urlString) | |
53 | if err != nil { | |
54 | fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) | |
55 | flag.Usage() | |
56 | } | |
57 | host = parsedUrl.Host | |
58 | useHttp = len(parsedUrl.Scheme) <= 0 || parsedUrl.Scheme == "http" | |
59 | } else if useHttp { | |
60 | _, err := url.Parse(fmt.Sprint("http://", host, ":", port)) | |
61 | if err != nil { | |
62 | fmt.Fprintln(os.Stderr, "Error parsing URL: ", err) | |
63 | flag.Usage() | |
64 | } | |
65 | } | |
66 | ||
67 | cmd := flag.Arg(0) | |
68 | var err error | |
69 | if useHttp { | |
70 | trans, err = thrift.NewTHttpClient(parsedUrl.String()) | |
71 | } else { | |
72 | portStr := fmt.Sprint(port) | |
73 | if strings.Contains(host, ":") { | |
74 | host, portStr, err = net.SplitHostPort(host) | |
75 | if err != nil { | |
76 | fmt.Fprintln(os.Stderr, "error with host:", err) | |
77 | os.Exit(1) | |
78 | } | |
79 | } | |
80 | trans, err = thrift.NewTSocket(net.JoinHostPort(host, portStr)) | |
81 | if err != nil { | |
82 | fmt.Fprintln(os.Stderr, "error resolving address:", err) | |
83 | os.Exit(1) | |
84 | } | |
85 | if framed { | |
86 | trans = thrift.NewTFramedTransport(trans) | |
87 | } | |
88 | } | |
89 | if err != nil { | |
90 | fmt.Fprintln(os.Stderr, "Error creating transport", err) | |
91 | os.Exit(1) | |
92 | } | |
93 | defer trans.Close() | |
94 | var protocolFactory thrift.TProtocolFactory | |
95 | switch protocol { | |
96 | case "compact": | |
97 | protocolFactory = thrift.NewTCompactProtocolFactory() | |
98 | break | |
99 | case "simplejson": | |
100 | protocolFactory = thrift.NewTSimpleJSONProtocolFactory() | |
101 | break | |
102 | case "json": | |
103 | protocolFactory = thrift.NewTJSONProtocolFactory() | |
104 | break | |
105 | case "binary", "": | |
106 | protocolFactory = thrift.NewTBinaryProtocolFactoryDefault() | |
107 | break | |
108 | default: | |
109 | fmt.Fprintln(os.Stderr, "Invalid protocol specified: ", protocol) | |
110 | Usage() | |
111 | os.Exit(1) | |
112 | } | |
113 | client := addsvc.NewAddServiceClientFactory(trans, protocolFactory) | |
114 | if err := trans.Open(); err != nil { | |
115 | fmt.Fprintln(os.Stderr, "Error opening socket to ", host, ":", port, " ", err) | |
116 | os.Exit(1) | |
117 | } | |
118 | ||
119 | switch cmd { | |
120 | case "Sum": | |
121 | if flag.NArg() - 1 != 2 { | |
122 | fmt.Fprintln(os.Stderr, "Sum requires 2 args") | |
123 | flag.Usage() | |
124 | } | |
125 | argvalue0, err6 := (strconv.ParseInt(flag.Arg(1), 10, 64)) | |
126 | if err6 != nil { | |
127 | Usage() | |
128 | return | |
129 | } | |
130 | value0 := argvalue0 | |
131 | argvalue1, err7 := (strconv.ParseInt(flag.Arg(2), 10, 64)) | |
132 | if err7 != nil { | |
133 | Usage() | |
134 | return | |
135 | } | |
136 | value1 := argvalue1 | |
137 | fmt.Print(client.Sum(context.Background(), value0, value1)) | |
138 | fmt.Print("\n") | |
139 | break | |
140 | case "Concat": | |
141 | if flag.NArg() - 1 != 2 { | |
142 | fmt.Fprintln(os.Stderr, "Concat requires 2 args") | |
143 | flag.Usage() | |
144 | } | |
145 | argvalue0 := flag.Arg(1) | |
146 | value0 := argvalue0 | |
147 | argvalue1 := flag.Arg(2) | |
148 | value1 := argvalue1 | |
149 | fmt.Print(client.Concat(context.Background(), value0, value1)) | |
150 | fmt.Print("\n") | |
151 | break | |
152 | case "": | |
153 | Usage() | |
154 | break | |
155 | default: | |
156 | fmt.Fprintln(os.Stderr, "Invalid function ", cmd) | |
157 | } | |
158 | } |
0 | // Autogenerated by Thrift Compiler (1.0.0-dev) | |
1 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING | |
2 | ||
3 | package addsvc | |
4 | ||
5 | import ( | |
6 | "bytes" | |
7 | "reflect" | |
8 | "context" | |
9 | "fmt" | |
10 | "github.com/apache/thrift/lib/go/thrift" | |
11 | ) | |
12 | ||
13 | // (needed to ensure safety because of naive import list construction.) | |
14 | var _ = thrift.ZERO | |
15 | var _ = fmt.Printf | |
16 | var _ = context.Background | |
17 | var _ = reflect.DeepEqual | |
18 | var _ = bytes.Equal | |
19 | ||
20 | ||
21 | func init() { | |
22 | } | |
23 |
0 | // Autogenerated by Thrift Compiler (1.0.0-dev) | |
1 | // DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING | |
2 | ||
3 | package addsvc | |
4 | ||
5 | import ( | |
6 | "bytes" | |
7 | "reflect" | |
8 | "context" | |
9 | "fmt" | |
10 | "github.com/apache/thrift/lib/go/thrift" | |
11 | ) | |
12 | ||
13 | // (needed to ensure safety because of naive import list construction.) | |
14 | var _ = thrift.ZERO | |
15 | var _ = fmt.Printf | |
16 | var _ = context.Background | |
17 | var _ = reflect.DeepEqual | |
18 | var _ = bytes.Equal | |
19 | ||
20 | // Attributes: | |
21 | // - Value | |
22 | // - Err | |
23 | type SumReply struct { | |
24 | Value int64 `thrift:"value,1" db:"value" json:"value"` | |
25 | Err string `thrift:"err,2" db:"err" json:"err"` | |
26 | } | |
27 | ||
28 | func NewSumReply() *SumReply { | |
29 | return &SumReply{} | |
30 | } | |
31 | ||
32 | ||
33 | func (p *SumReply) GetValue() int64 { | |
34 | return p.Value | |
35 | } | |
36 | ||
37 | func (p *SumReply) GetErr() string { | |
38 | return p.Err | |
39 | } | |
40 | func (p *SumReply) Read(iprot thrift.TProtocol) error { | |
41 | if _, err := iprot.ReadStructBegin(); err != nil { | |
42 | return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) | |
43 | } | |
44 | ||
45 | ||
46 | for { | |
47 | _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() | |
48 | if err != nil { | |
49 | return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) | |
50 | } | |
51 | if fieldTypeId == thrift.STOP { break; } | |
52 | switch fieldId { | |
53 | case 1: | |
54 | if fieldTypeId == thrift.I64 { | |
55 | if err := p.ReadField1(iprot); err != nil { | |
56 | return err | |
57 | } | |
58 | } else { | |
59 | if err := iprot.Skip(fieldTypeId); err != nil { | |
60 | return err | |
61 | } | |
62 | } | |
63 | case 2: | |
64 | if fieldTypeId == thrift.STRING { | |
65 | if err := p.ReadField2(iprot); err != nil { | |
66 | return err | |
67 | } | |
68 | } else { | |
69 | if err := iprot.Skip(fieldTypeId); err != nil { | |
70 | return err | |
71 | } | |
72 | } | |
73 | default: | |
74 | if err := iprot.Skip(fieldTypeId); err != nil { | |
75 | return err | |
76 | } | |
77 | } | |
78 | if err := iprot.ReadFieldEnd(); err != nil { | |
79 | return err | |
80 | } | |
81 | } | |
82 | if err := iprot.ReadStructEnd(); err != nil { | |
83 | return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) | |
84 | } | |
85 | return nil | |
86 | } | |
87 | ||
88 | func (p *SumReply) ReadField1(iprot thrift.TProtocol) error { | |
89 | if v, err := iprot.ReadI64(); err != nil { | |
90 | return thrift.PrependError("error reading field 1: ", err) | |
91 | } else { | |
92 | p.Value = v | |
93 | } | |
94 | return nil | |
95 | } | |
96 | ||
97 | func (p *SumReply) ReadField2(iprot thrift.TProtocol) error { | |
98 | if v, err := iprot.ReadString(); err != nil { | |
99 | return thrift.PrependError("error reading field 2: ", err) | |
100 | } else { | |
101 | p.Err = v | |
102 | } | |
103 | return nil | |
104 | } | |
105 | ||
106 | func (p *SumReply) Write(oprot thrift.TProtocol) error { | |
107 | if err := oprot.WriteStructBegin("SumReply"); err != nil { | |
108 | return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } | |
109 | if p != nil { | |
110 | if err := p.writeField1(oprot); err != nil { return err } | |
111 | if err := p.writeField2(oprot); err != nil { return err } | |
112 | } | |
113 | if err := oprot.WriteFieldStop(); err != nil { | |
114 | return thrift.PrependError("write field stop error: ", err) } | |
115 | if err := oprot.WriteStructEnd(); err != nil { | |
116 | return thrift.PrependError("write struct stop error: ", err) } | |
117 | return nil | |
118 | } | |
119 | ||
120 | func (p *SumReply) writeField1(oprot thrift.TProtocol) (err error) { | |
121 | if err := oprot.WriteFieldBegin("value", thrift.I64, 1); err != nil { | |
122 | return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:value: ", p), err) } | |
123 | if err := oprot.WriteI64(int64(p.Value)); err != nil { | |
124 | return thrift.PrependError(fmt.Sprintf("%T.value (1) field write error: ", p), err) } | |
125 | if err := oprot.WriteFieldEnd(); err != nil { | |
126 | return thrift.PrependError(fmt.Sprintf("%T write field end error 1:value: ", p), err) } | |
127 | return err | |
128 | } | |
129 | ||
130 | func (p *SumReply) writeField2(oprot thrift.TProtocol) (err error) { | |
131 | if err := oprot.WriteFieldBegin("err", thrift.STRING, 2); err != nil { | |
132 | return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err) } | |
133 | if err := oprot.WriteString(string(p.Err)); err != nil { | |
134 | return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err) } | |
135 | if err := oprot.WriteFieldEnd(); err != nil { | |
136 | return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err) } | |
137 | return err | |
138 | } | |
139 | ||
140 | func (p *SumReply) String() string { | |
141 | if p == nil { | |
142 | return "<nil>" | |
143 | } | |
144 | return fmt.Sprintf("SumReply(%+v)", *p) | |
145 | } | |
146 | ||
147 | // Attributes: | |
148 | // - Value | |
149 | // - Err | |
150 | type ConcatReply struct { | |
151 | Value string `thrift:"value,1" db:"value" json:"value"` | |
152 | Err string `thrift:"err,2" db:"err" json:"err"` | |
153 | } | |
154 | ||
155 | func NewConcatReply() *ConcatReply { | |
156 | return &ConcatReply{} | |
157 | } | |
158 | ||
159 | ||
160 | func (p *ConcatReply) GetValue() string { | |
161 | return p.Value | |
162 | } | |
163 | ||
164 | func (p *ConcatReply) GetErr() string { | |
165 | return p.Err | |
166 | } | |
167 | func (p *ConcatReply) Read(iprot thrift.TProtocol) error { | |
168 | if _, err := iprot.ReadStructBegin(); err != nil { | |
169 | return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) | |
170 | } | |
171 | ||
172 | ||
173 | for { | |
174 | _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() | |
175 | if err != nil { | |
176 | return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) | |
177 | } | |
178 | if fieldTypeId == thrift.STOP { break; } | |
179 | switch fieldId { | |
180 | case 1: | |
181 | if fieldTypeId == thrift.STRING { | |
182 | if err := p.ReadField1(iprot); err != nil { | |
183 | return err | |
184 | } | |
185 | } else { | |
186 | if err := iprot.Skip(fieldTypeId); err != nil { | |
187 | return err | |
188 | } | |
189 | } | |
190 | case 2: | |
191 | if fieldTypeId == thrift.STRING { | |
192 | if err := p.ReadField2(iprot); err != nil { | |
193 | return err | |
194 | } | |
195 | } else { | |
196 | if err := iprot.Skip(fieldTypeId); err != nil { | |
197 | return err | |
198 | } | |
199 | } | |
200 | default: | |
201 | if err := iprot.Skip(fieldTypeId); err != nil { | |
202 | return err | |
203 | } | |
204 | } | |
205 | if err := iprot.ReadFieldEnd(); err != nil { | |
206 | return err | |
207 | } | |
208 | } | |
209 | if err := iprot.ReadStructEnd(); err != nil { | |
210 | return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) | |
211 | } | |
212 | return nil | |
213 | } | |
214 | ||
215 | func (p *ConcatReply) ReadField1(iprot thrift.TProtocol) error { | |
216 | if v, err := iprot.ReadString(); err != nil { | |
217 | return thrift.PrependError("error reading field 1: ", err) | |
218 | } else { | |
219 | p.Value = v | |
220 | } | |
221 | return nil | |
222 | } | |
223 | ||
224 | func (p *ConcatReply) ReadField2(iprot thrift.TProtocol) error { | |
225 | if v, err := iprot.ReadString(); err != nil { | |
226 | return thrift.PrependError("error reading field 2: ", err) | |
227 | } else { | |
228 | p.Err = v | |
229 | } | |
230 | return nil | |
231 | } | |
232 | ||
233 | func (p *ConcatReply) Write(oprot thrift.TProtocol) error { | |
234 | if err := oprot.WriteStructBegin("ConcatReply"); err != nil { | |
235 | return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } | |
236 | if p != nil { | |
237 | if err := p.writeField1(oprot); err != nil { return err } | |
238 | if err := p.writeField2(oprot); err != nil { return err } | |
239 | } | |
240 | if err := oprot.WriteFieldStop(); err != nil { | |
241 | return thrift.PrependError("write field stop error: ", err) } | |
242 | if err := oprot.WriteStructEnd(); err != nil { | |
243 | return thrift.PrependError("write struct stop error: ", err) } | |
244 | return nil | |
245 | } | |
246 | ||
247 | func (p *ConcatReply) writeField1(oprot thrift.TProtocol) (err error) { | |
248 | if err := oprot.WriteFieldBegin("value", thrift.STRING, 1); err != nil { | |
249 | return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:value: ", p), err) } | |
250 | if err := oprot.WriteString(string(p.Value)); err != nil { | |
251 | return thrift.PrependError(fmt.Sprintf("%T.value (1) field write error: ", p), err) } | |
252 | if err := oprot.WriteFieldEnd(); err != nil { | |
253 | return thrift.PrependError(fmt.Sprintf("%T write field end error 1:value: ", p), err) } | |
254 | return err | |
255 | } | |
256 | ||
257 | func (p *ConcatReply) writeField2(oprot thrift.TProtocol) (err error) { | |
258 | if err := oprot.WriteFieldBegin("err", thrift.STRING, 2); err != nil { | |
259 | return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:err: ", p), err) } | |
260 | if err := oprot.WriteString(string(p.Err)); err != nil { | |
261 | return thrift.PrependError(fmt.Sprintf("%T.err (2) field write error: ", p), err) } | |
262 | if err := oprot.WriteFieldEnd(); err != nil { | |
263 | return thrift.PrependError(fmt.Sprintf("%T write field end error 2:err: ", p), err) } | |
264 | return err | |
265 | } | |
266 | ||
267 | func (p *ConcatReply) String() string { | |
268 | if p == nil { | |
269 | return "<nil>" | |
270 | } | |
271 | return fmt.Sprintf("ConcatReply(%+v)", *p) | |
272 | } | |
273 | ||
274 | type AddService interface { | |
275 | // Parameters: | |
276 | // - A | |
277 | // - B | |
278 | Sum(ctx context.Context, a int64, b int64) (r *SumReply, err error) | |
279 | // Parameters: | |
280 | // - A | |
281 | // - B | |
282 | Concat(ctx context.Context, a string, b string) (r *ConcatReply, err error) | |
283 | } | |
284 | ||
285 | type AddServiceClient struct { | |
286 | Transport thrift.TTransport | |
287 | ProtocolFactory thrift.TProtocolFactory | |
288 | InputProtocol thrift.TProtocol | |
289 | OutputProtocol thrift.TProtocol | |
290 | SeqId int32 | |
291 | } | |
292 | ||
293 | func NewAddServiceClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AddServiceClient { | |
294 | return &AddServiceClient{Transport: t, | |
295 | ProtocolFactory: f, | |
296 | InputProtocol: f.GetProtocol(t), | |
297 | OutputProtocol: f.GetProtocol(t), | |
298 | SeqId: 0, | |
299 | } | |
300 | } | |
301 | ||
302 | func NewAddServiceClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AddServiceClient { | |
303 | return &AddServiceClient{Transport: t, | |
304 | ProtocolFactory: nil, | |
305 | InputProtocol: iprot, | |
306 | OutputProtocol: oprot, | |
307 | SeqId: 0, | |
308 | } | |
309 | } | |
310 | ||
311 | // Parameters: | |
312 | // - A | |
313 | // - B | |
314 | func (p *AddServiceClient) Sum(ctx context.Context, a int64, b int64) (r *SumReply, err error) { | |
315 | if err = p.sendSum(a, b); err != nil { return } | |
316 | return p.recvSum() | |
317 | } | |
318 | ||
319 | func (p *AddServiceClient) sendSum(a int64, b int64)(err error) { | |
320 | oprot := p.OutputProtocol | |
321 | if oprot == nil { | |
322 | oprot = p.ProtocolFactory.GetProtocol(p.Transport) | |
323 | p.OutputProtocol = oprot | |
324 | } | |
325 | p.SeqId++ | |
326 | if err = oprot.WriteMessageBegin("Sum", thrift.CALL, p.SeqId); err != nil { | |
327 | return | |
328 | } | |
329 | args := AddServiceSumArgs{ | |
330 | A : a, | |
331 | B : b, | |
332 | } | |
333 | if err = args.Write(oprot); err != nil { | |
334 | return | |
335 | } | |
336 | if err = oprot.WriteMessageEnd(); err != nil { | |
337 | return | |
338 | } | |
339 | return oprot.Flush() | |
340 | } | |
341 | ||
342 | ||
343 | func (p *AddServiceClient) recvSum() (value *SumReply, err error) { | |
344 | iprot := p.InputProtocol | |
345 | if iprot == nil { | |
346 | iprot = p.ProtocolFactory.GetProtocol(p.Transport) | |
347 | p.InputProtocol = iprot | |
348 | } | |
349 | method, mTypeId, seqId, err := iprot.ReadMessageBegin() | |
350 | if err != nil { | |
351 | return | |
352 | } | |
353 | if method != "Sum" { | |
354 | err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Sum failed: wrong method name") | |
355 | return | |
356 | } | |
357 | if p.SeqId != seqId { | |
358 | err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Sum failed: out of sequence response") | |
359 | return | |
360 | } | |
361 | if mTypeId == thrift.EXCEPTION { | |
362 | error0 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") | |
363 | var error1 error | |
364 | error1, err = error0.Read(iprot) | |
365 | if err != nil { | |
366 | return | |
367 | } | |
368 | if err = iprot.ReadMessageEnd(); err != nil { | |
369 | return | |
370 | } | |
371 | err = error1 | |
372 | return | |
373 | } | |
374 | if mTypeId != thrift.REPLY { | |
375 | err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Sum failed: invalid message type") | |
376 | return | |
377 | } | |
378 | result := AddServiceSumResult{} | |
379 | if err = result.Read(iprot); err != nil { | |
380 | return | |
381 | } | |
382 | if err = iprot.ReadMessageEnd(); err != nil { | |
383 | return | |
384 | } | |
385 | value = result.GetSuccess() | |
386 | return | |
387 | } | |
388 | ||
389 | // Parameters: | |
390 | // - A | |
391 | // - B | |
392 | func (p *AddServiceClient) Concat(ctx context.Context, a string, b string) (r *ConcatReply, err error) { | |
393 | if err = p.sendConcat(a, b); err != nil { return } | |
394 | return p.recvConcat() | |
395 | } | |
396 | ||
397 | func (p *AddServiceClient) sendConcat(a string, b string)(err error) { | |
398 | oprot := p.OutputProtocol | |
399 | if oprot == nil { | |
400 | oprot = p.ProtocolFactory.GetProtocol(p.Transport) | |
401 | p.OutputProtocol = oprot | |
402 | } | |
403 | p.SeqId++ | |
404 | if err = oprot.WriteMessageBegin("Concat", thrift.CALL, p.SeqId); err != nil { | |
405 | return | |
406 | } | |
407 | args := AddServiceConcatArgs{ | |
408 | A : a, | |
409 | B : b, | |
410 | } | |
411 | if err = args.Write(oprot); err != nil { | |
412 | return | |
413 | } | |
414 | if err = oprot.WriteMessageEnd(); err != nil { | |
415 | return | |
416 | } | |
417 | return oprot.Flush() | |
418 | } | |
419 | ||
420 | ||
421 | func (p *AddServiceClient) recvConcat() (value *ConcatReply, err error) { | |
422 | iprot := p.InputProtocol | |
423 | if iprot == nil { | |
424 | iprot = p.ProtocolFactory.GetProtocol(p.Transport) | |
425 | p.InputProtocol = iprot | |
426 | } | |
427 | method, mTypeId, seqId, err := iprot.ReadMessageBegin() | |
428 | if err != nil { | |
429 | return | |
430 | } | |
431 | if method != "Concat" { | |
432 | err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "Concat failed: wrong method name") | |
433 | return | |
434 | } | |
435 | if p.SeqId != seqId { | |
436 | err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "Concat failed: out of sequence response") | |
437 | return | |
438 | } | |
439 | if mTypeId == thrift.EXCEPTION { | |
440 | error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") | |
441 | var error3 error | |
442 | error3, err = error2.Read(iprot) | |
443 | if err != nil { | |
444 | return | |
445 | } | |
446 | if err = iprot.ReadMessageEnd(); err != nil { | |
447 | return | |
448 | } | |
449 | err = error3 | |
450 | return | |
451 | } | |
452 | if mTypeId != thrift.REPLY { | |
453 | err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "Concat failed: invalid message type") | |
454 | return | |
455 | } | |
456 | result := AddServiceConcatResult{} | |
457 | if err = result.Read(iprot); err != nil { | |
458 | return | |
459 | } | |
460 | if err = iprot.ReadMessageEnd(); err != nil { | |
461 | return | |
462 | } | |
463 | value = result.GetSuccess() | |
464 | return | |
465 | } | |
466 | ||
467 | ||
468 | type AddServiceProcessor struct { | |
469 | processorMap map[string]thrift.TProcessorFunction | |
470 | handler AddService | |
471 | } | |
472 | ||
473 | func (p *AddServiceProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { | |
474 | p.processorMap[key] = processor | |
475 | } | |
476 | ||
477 | func (p *AddServiceProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { | |
478 | processor, ok = p.processorMap[key] | |
479 | return processor, ok | |
480 | } | |
481 | ||
482 | func (p *AddServiceProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { | |
483 | return p.processorMap | |
484 | } | |
485 | ||
486 | func NewAddServiceProcessor(handler AddService) *AddServiceProcessor { | |
487 | ||
488 | self4 := &AddServiceProcessor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)} | |
489 | self4.processorMap["Sum"] = &addServiceProcessorSum{handler:handler} | |
490 | self4.processorMap["Concat"] = &addServiceProcessorConcat{handler:handler} | |
491 | return self4 | |
492 | } | |
493 | ||
494 | func (p *AddServiceProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { | |
495 | name, _, seqId, err := iprot.ReadMessageBegin() | |
496 | if err != nil { return false, err } | |
497 | if processor, ok := p.GetProcessorFunction(name); ok { | |
498 | return processor.Process(ctx, seqId, iprot, oprot) | |
499 | } | |
500 | iprot.Skip(thrift.STRUCT) | |
501 | iprot.ReadMessageEnd() | |
502 | x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function " + name) | |
503 | oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) | |
504 | x5.Write(oprot) | |
505 | oprot.WriteMessageEnd() | |
506 | oprot.Flush() | |
507 | return false, x5 | |
508 | ||
509 | } | |
510 | ||
511 | type addServiceProcessorSum struct { | |
512 | handler AddService | |
513 | } | |
514 | ||
515 | func (p *addServiceProcessorSum) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { | |
516 | args := AddServiceSumArgs{} | |
517 | if err = args.Read(iprot); err != nil { | |
518 | iprot.ReadMessageEnd() | |
519 | x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) | |
520 | oprot.WriteMessageBegin("Sum", thrift.EXCEPTION, seqId) | |
521 | x.Write(oprot) | |
522 | oprot.WriteMessageEnd() | |
523 | oprot.Flush() | |
524 | return false, err | |
525 | } | |
526 | ||
527 | iprot.ReadMessageEnd() | |
528 | result := AddServiceSumResult{} | |
529 | var retval *SumReply | |
530 | var err2 error | |
531 | if retval, err2 = p.handler.Sum(ctx, args.A, args.B); err2 != nil { | |
532 | x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Sum: " + err2.Error()) | |
533 | oprot.WriteMessageBegin("Sum", thrift.EXCEPTION, seqId) | |
534 | x.Write(oprot) | |
535 | oprot.WriteMessageEnd() | |
536 | oprot.Flush() | |
537 | return true, err2 | |
538 | } else { | |
539 | result.Success = retval | |
540 | } | |
541 | if err2 = oprot.WriteMessageBegin("Sum", thrift.REPLY, seqId); err2 != nil { | |
542 | err = err2 | |
543 | } | |
544 | if err2 = result.Write(oprot); err == nil && err2 != nil { | |
545 | err = err2 | |
546 | } | |
547 | if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { | |
548 | err = err2 | |
549 | } | |
550 | if err2 = oprot.Flush(); err == nil && err2 != nil { | |
551 | err = err2 | |
552 | } | |
553 | if err != nil { | |
554 | return | |
555 | } | |
556 | return true, err | |
557 | } | |
558 | ||
559 | type addServiceProcessorConcat struct { | |
560 | handler AddService | |
561 | } | |
562 | ||
563 | func (p *addServiceProcessorConcat) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { | |
564 | args := AddServiceConcatArgs{} | |
565 | if err = args.Read(iprot); err != nil { | |
566 | iprot.ReadMessageEnd() | |
567 | x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) | |
568 | oprot.WriteMessageBegin("Concat", thrift.EXCEPTION, seqId) | |
569 | x.Write(oprot) | |
570 | oprot.WriteMessageEnd() | |
571 | oprot.Flush() | |
572 | return false, err | |
573 | } | |
574 | ||
575 | iprot.ReadMessageEnd() | |
576 | result := AddServiceConcatResult{} | |
577 | var retval *ConcatReply | |
578 | var err2 error | |
579 | if retval, err2 = p.handler.Concat(ctx, args.A, args.B); err2 != nil { | |
580 | x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing Concat: " + err2.Error()) | |
581 | oprot.WriteMessageBegin("Concat", thrift.EXCEPTION, seqId) | |
582 | x.Write(oprot) | |
583 | oprot.WriteMessageEnd() | |
584 | oprot.Flush() | |
585 | return true, err2 | |
586 | } else { | |
587 | result.Success = retval | |
588 | } | |
589 | if err2 = oprot.WriteMessageBegin("Concat", thrift.REPLY, seqId); err2 != nil { | |
590 | err = err2 | |
591 | } | |
592 | if err2 = result.Write(oprot); err == nil && err2 != nil { | |
593 | err = err2 | |
594 | } | |
595 | if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { | |
596 | err = err2 | |
597 | } | |
598 | if err2 = oprot.Flush(); err == nil && err2 != nil { | |
599 | err = err2 | |
600 | } | |
601 | if err != nil { | |
602 | return | |
603 | } | |
604 | return true, err | |
605 | } | |
606 | ||
607 | ||
608 | // HELPER FUNCTIONS AND STRUCTURES | |
609 | ||
610 | // Attributes: | |
611 | // - A | |
612 | // - B | |
613 | type AddServiceSumArgs struct { | |
614 | A int64 `thrift:"a,1" db:"a" json:"a"` | |
615 | B int64 `thrift:"b,2" db:"b" json:"b"` | |
616 | } | |
617 | ||
618 | func NewAddServiceSumArgs() *AddServiceSumArgs { | |
619 | return &AddServiceSumArgs{} | |
620 | } | |
621 | ||
622 | ||
623 | func (p *AddServiceSumArgs) GetA() int64 { | |
624 | return p.A | |
625 | } | |
626 | ||
627 | func (p *AddServiceSumArgs) GetB() int64 { | |
628 | return p.B | |
629 | } | |
630 | func (p *AddServiceSumArgs) Read(iprot thrift.TProtocol) error { | |
631 | if _, err := iprot.ReadStructBegin(); err != nil { | |
632 | return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) | |
633 | } | |
634 | ||
635 | ||
636 | for { | |
637 | _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() | |
638 | if err != nil { | |
639 | return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) | |
640 | } | |
641 | if fieldTypeId == thrift.STOP { break; } | |
642 | switch fieldId { | |
643 | case 1: | |
644 | if fieldTypeId == thrift.I64 { | |
645 | if err := p.ReadField1(iprot); err != nil { | |
646 | return err | |
647 | } | |
648 | } else { | |
649 | if err := iprot.Skip(fieldTypeId); err != nil { | |
650 | return err | |
651 | } | |
652 | } | |
653 | case 2: | |
654 | if fieldTypeId == thrift.I64 { | |
655 | if err := p.ReadField2(iprot); err != nil { | |
656 | return err | |
657 | } | |
658 | } else { | |
659 | if err := iprot.Skip(fieldTypeId); err != nil { | |
660 | return err | |
661 | } | |
662 | } | |
663 | default: | |
664 | if err := iprot.Skip(fieldTypeId); err != nil { | |
665 | return err | |
666 | } | |
667 | } | |
668 | if err := iprot.ReadFieldEnd(); err != nil { | |
669 | return err | |
670 | } | |
671 | } | |
672 | if err := iprot.ReadStructEnd(); err != nil { | |
673 | return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) | |
674 | } | |
675 | return nil | |
676 | } | |
677 | ||
678 | func (p *AddServiceSumArgs) ReadField1(iprot thrift.TProtocol) error { | |
679 | if v, err := iprot.ReadI64(); err != nil { | |
680 | return thrift.PrependError("error reading field 1: ", err) | |
681 | } else { | |
682 | p.A = v | |
683 | } | |
684 | return nil | |
685 | } | |
686 | ||
687 | func (p *AddServiceSumArgs) ReadField2(iprot thrift.TProtocol) error { | |
688 |