diff --git a/Gopkg.lock b/Gopkg.lock index 24f8499..563de19 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -206,10 +206,12 @@ branch = "master" name = "github.com/golang/protobuf" packages = [ + "jsonpb", "proto", "ptypes", "ptypes/any", "ptypes/duration", + "ptypes/struct", "ptypes/timestamp" ] revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845" @@ -589,6 +591,16 @@ revision = "98aa888b79d8de04afe0fccf45ed10594efc858b" version = "v1.1" +[[projects]] + name = "github.com/twitchtv/twirp" + packages = [ + ".", + "ctxsetters", + "internal/contextkeys" + ] + revision = "db96cdf354e8dc053e5ee5fe890bb0a7f18123ab" + version = "v5.0.0" + [[projects]] name = "github.com/ulikunitz/xz" packages = [ @@ -753,6 +765,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "97c8282ef9b3abed71907d17ccf38379134714596610880b02d5ca03be634678" + inputs-digest = "51866d1bd0089290b4562a563c65db61b4973e66be0e14297f0680059dbdf138" solver-name = "gps-cdcl" solver-version = 1 diff --git a/mage.go b/mage.go index e0efb0d..11a0de8 100644 --- a/mage.go +++ b/mage.go @@ -164,8 +164,7 @@ func Tools(ctx context.Context) { tools := []string{ "github.com/golang/dep/cmd/dep", "github.com/golang/protobuf/protoc-gen-go", - // "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", - // "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", + "github.com/twitchtv/twirp/protoc-gen-twirp", } for _, t := range tools { diff --git a/proto/client/client.go b/proto/client/client.go deleted file mode 100644 index b3c74f9..0000000 --- a/proto/client/client.go +++ /dev/null @@ -1 +0,0 @@ -package routeclient diff --git a/proto/client/doc.go b/proto/client/doc.go deleted file mode 100644 index 1c1f23d..0000000 --- a/proto/client/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package routeclient is a higer level convenience wrapper around the RPC layer for route. -package routeclient diff --git a/proto/regen.sh b/proto/regen.sh index fdc1a10..3e1ec53 100755 --- a/proto/regen.sh +++ b/proto/regen.sh @@ -1,7 +1,6 @@ #!/bin/bash -protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --go_out=Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:. \ +protoc -I. \ + --go_out=:. \ + --twirp_out=. \ route.proto diff --git a/proto/route.pb.go b/proto/route.pb.go index 0373a0e..7567e00 100644 --- a/proto/route.pb.go +++ b/proto/route.pb.go @@ -26,11 +26,6 @@ import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) - // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf @@ -352,478 +347,6 @@ func init() { proto.RegisterType((*BackendID)(nil), "route.BackendID") } -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// Client API for Routes service - -type RoutesClient interface { - // Get fetches a single route based on the Host or ID. - Get(ctx context.Context, in *GetRouteRequest, opts ...grpc.CallOption) (*Route, error) - // GetAll fetches all of the routes that the user owns. - GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*GetAllRoutesResponse, error) - // Put creates a new route based on user-supplied details. - Put(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Route, error) - // Delete removes a route. - Delete(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Nil, error) -} - -type routesClient struct { - cc *grpc.ClientConn -} - -func NewRoutesClient(cc *grpc.ClientConn) RoutesClient { - return &routesClient{cc} -} - -func (c *routesClient) Get(ctx context.Context, in *GetRouteRequest, opts ...grpc.CallOption) (*Route, error) { - out := new(Route) - err := grpc.Invoke(ctx, "/route.Routes/Get", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *routesClient) GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*GetAllRoutesResponse, error) { - out := new(GetAllRoutesResponse) - err := grpc.Invoke(ctx, "/route.Routes/GetAll", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *routesClient) Put(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Route, error) { - out := new(Route) - err := grpc.Invoke(ctx, "/route.Routes/Put", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *routesClient) Delete(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Nil, error) { - out := new(Nil) - err := grpc.Invoke(ctx, "/route.Routes/Delete", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Routes service - -type RoutesServer interface { - // Get fetches a single route based on the Host or ID. - Get(context.Context, *GetRouteRequest) (*Route, error) - // GetAll fetches all of the routes that the user owns. - GetAll(context.Context, *Nil) (*GetAllRoutesResponse, error) - // Put creates a new route based on user-supplied details. - Put(context.Context, *Route) (*Route, error) - // Delete removes a route. - Delete(context.Context, *Route) (*Nil, error) -} - -func RegisterRoutesServer(s *grpc.Server, srv RoutesServer) { - s.RegisterService(&_Routes_serviceDesc, srv) -} - -func _Routes_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRouteRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutesServer).Get(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Routes/Get", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutesServer).Get(ctx, req.(*GetRouteRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Routes_GetAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Nil) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutesServer).GetAll(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Routes/GetAll", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutesServer).GetAll(ctx, req.(*Nil)) - } - return interceptor(ctx, in, info, handler) -} - -func _Routes_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Route) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutesServer).Put(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Routes/Put", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutesServer).Put(ctx, req.(*Route)) - } - return interceptor(ctx, in, info, handler) -} - -func _Routes_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Route) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(RoutesServer).Delete(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Routes/Delete", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(RoutesServer).Delete(ctx, req.(*Route)) - } - return interceptor(ctx, in, info, handler) -} - -var _Routes_serviceDesc = grpc.ServiceDesc{ - ServiceName: "route.Routes", - HandlerType: (*RoutesServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Get", - Handler: _Routes_Get_Handler, - }, - { - MethodName: "GetAll", - Handler: _Routes_GetAll_Handler, - }, - { - MethodName: "Put", - Handler: _Routes_Put_Handler, - }, - { - MethodName: "Delete", - Handler: _Routes_Delete_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "route.proto", -} - -// Client API for Tokens service - -type TokensClient interface { - Get(ctx context.Context, in *GetTokenRequest, opts ...grpc.CallOption) (*Token, error) - GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*TokenSet, error) - Put(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Token, error) - Delete(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error) - Deactivate(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error) -} - -type tokensClient struct { - cc *grpc.ClientConn -} - -func NewTokensClient(cc *grpc.ClientConn) TokensClient { - return &tokensClient{cc} -} - -func (c *tokensClient) Get(ctx context.Context, in *GetTokenRequest, opts ...grpc.CallOption) (*Token, error) { - out := new(Token) - err := grpc.Invoke(ctx, "/route.Tokens/Get", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *tokensClient) GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*TokenSet, error) { - out := new(TokenSet) - err := grpc.Invoke(ctx, "/route.Tokens/GetAll", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *tokensClient) Put(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Token, error) { - out := new(Token) - err := grpc.Invoke(ctx, "/route.Tokens/Put", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *tokensClient) Delete(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error) { - out := new(Nil) - err := grpc.Invoke(ctx, "/route.Tokens/Delete", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *tokensClient) Deactivate(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error) { - out := new(Nil) - err := grpc.Invoke(ctx, "/route.Tokens/Deactivate", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Tokens service - -type TokensServer interface { - Get(context.Context, *GetTokenRequest) (*Token, error) - GetAll(context.Context, *Nil) (*TokenSet, error) - Put(context.Context, *Token) (*Token, error) - Delete(context.Context, *Token) (*Nil, error) - Deactivate(context.Context, *Token) (*Nil, error) -} - -func RegisterTokensServer(s *grpc.Server, srv TokensServer) { - s.RegisterService(&_Tokens_serviceDesc, srv) -} - -func _Tokens_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetTokenRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TokensServer).Get(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Tokens/Get", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TokensServer).Get(ctx, req.(*GetTokenRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Tokens_GetAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Nil) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TokensServer).GetAll(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Tokens/GetAll", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TokensServer).GetAll(ctx, req.(*Nil)) - } - return interceptor(ctx, in, info, handler) -} - -func _Tokens_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Token) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TokensServer).Put(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Tokens/Put", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TokensServer).Put(ctx, req.(*Token)) - } - return interceptor(ctx, in, info, handler) -} - -func _Tokens_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Token) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TokensServer).Delete(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Tokens/Delete", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TokensServer).Delete(ctx, req.(*Token)) - } - return interceptor(ctx, in, info, handler) -} - -func _Tokens_Deactivate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Token) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(TokensServer).Deactivate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Tokens/Deactivate", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TokensServer).Deactivate(ctx, req.(*Token)) - } - return interceptor(ctx, in, info, handler) -} - -var _Tokens_serviceDesc = grpc.ServiceDesc{ - ServiceName: "route.Tokens", - HandlerType: (*TokensServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Get", - Handler: _Tokens_Get_Handler, - }, - { - MethodName: "GetAll", - Handler: _Tokens_GetAll_Handler, - }, - { - MethodName: "Put", - Handler: _Tokens_Put_Handler, - }, - { - MethodName: "Delete", - Handler: _Tokens_Delete_Handler, - }, - { - MethodName: "Deactivate", - Handler: _Tokens_Deactivate_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "route.proto", -} - -// Client API for Backends service - -type BackendsClient interface { - List(ctx context.Context, in *BackendSelector, opts ...grpc.CallOption) (*BackendList, error) - Kill(ctx context.Context, in *BackendID, opts ...grpc.CallOption) (*Nil, error) -} - -type backendsClient struct { - cc *grpc.ClientConn -} - -func NewBackendsClient(cc *grpc.ClientConn) BackendsClient { - return &backendsClient{cc} -} - -func (c *backendsClient) List(ctx context.Context, in *BackendSelector, opts ...grpc.CallOption) (*BackendList, error) { - out := new(BackendList) - err := grpc.Invoke(ctx, "/route.Backends/List", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *backendsClient) Kill(ctx context.Context, in *BackendID, opts ...grpc.CallOption) (*Nil, error) { - out := new(Nil) - err := grpc.Invoke(ctx, "/route.Backends/Kill", in, out, c.cc, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Backends service - -type BackendsServer interface { - List(context.Context, *BackendSelector) (*BackendList, error) - Kill(context.Context, *BackendID) (*Nil, error) -} - -func RegisterBackendsServer(s *grpc.Server, srv BackendsServer) { - s.RegisterService(&_Backends_serviceDesc, srv) -} - -func _Backends_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BackendSelector) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BackendsServer).List(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Backends/List", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BackendsServer).List(ctx, req.(*BackendSelector)) - } - return interceptor(ctx, in, info, handler) -} - -func _Backends_Kill_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BackendID) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BackendsServer).Kill(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/route.Backends/Kill", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BackendsServer).Kill(ctx, req.(*BackendID)) - } - return interceptor(ctx, in, info, handler) -} - -var _Backends_serviceDesc = grpc.ServiceDesc{ - ServiceName: "route.Backends", - HandlerType: (*BackendsServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "List", - Handler: _Backends_List_Handler, - }, - { - MethodName: "Kill", - Handler: _Backends_Kill_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "route.proto", -} - func init() { proto.RegisterFile("route.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ diff --git a/proto/route.twirp.go b/proto/route.twirp.go new file mode 100644 index 0000000..8051e58 --- /dev/null +++ b/proto/route.twirp.go @@ -0,0 +1,2486 @@ +// Code generated by protoc-gen-twirp v5.0.0, DO NOT EDIT. +// source: route.proto + +/* +Package route is a generated twirp stub package. +This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v5.0.0. + +It is generated from these files: + route.proto +*/ +package route + +import bytes "bytes" +import context "context" +import fmt "fmt" +import ioutil "io/ioutil" +import log "log" +import http "net/http" + +import jsonpb "github.com/golang/protobuf/jsonpb" +import proto "github.com/golang/protobuf/proto" +import twirp "github.com/twitchtv/twirp" +import ctxsetters "github.com/twitchtv/twirp/ctxsetters" + +// Imports only used by utility functions: +import io "io" +import strconv "strconv" +import json "encoding/json" +import url "net/url" + +// ================ +// Routes Interface +// ================ + +// Routes lets users manage and manipulate http routes. +type Routes interface { + // Get fetches a single route based on the Host or ID. + Get(context.Context, *GetRouteRequest) (*Route, error) + + // GetAll fetches all of the routes that the user owns. + GetAll(context.Context, *Nil) (*GetAllRoutesResponse, error) + + // Put creates a new route based on user-supplied details. + Put(context.Context, *Route) (*Route, error) + + // Delete removes a route. + Delete(context.Context, *Route) (*Nil, error) +} + +// ====================== +// Routes Protobuf Client +// ====================== + +type routesProtobufClient struct { + urlBase string + client *http.Client +} + +// NewRoutesProtobufClient creates a Protobuf client that implements the Routes interface. +// It communicates using protobuf messages and can be configured with a custom http.Client. +func NewRoutesProtobufClient(addr string, client *http.Client) Routes { + return &routesProtobufClient{ + urlBase: urlBase(addr), + client: withoutRedirects(client), + } +} + +func (c *routesProtobufClient) Get(ctx context.Context, in *GetRouteRequest) (*Route, error) { + url := c.urlBase + RoutesPathPrefix + "Get" + out := new(Route) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *routesProtobufClient) GetAll(ctx context.Context, in *Nil) (*GetAllRoutesResponse, error) { + url := c.urlBase + RoutesPathPrefix + "GetAll" + out := new(GetAllRoutesResponse) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *routesProtobufClient) Put(ctx context.Context, in *Route) (*Route, error) { + url := c.urlBase + RoutesPathPrefix + "Put" + out := new(Route) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *routesProtobufClient) Delete(ctx context.Context, in *Route) (*Nil, error) { + url := c.urlBase + RoutesPathPrefix + "Delete" + out := new(Nil) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +// ================== +// Routes JSON Client +// ================== + +type routesJSONClient struct { + urlBase string + client *http.Client +} + +// NewRoutesJSONClient creates a JSON client that implements the Routes interface. +// It communicates using JSON requests and responses instead of protobuf messages. +func NewRoutesJSONClient(addr string, client *http.Client) Routes { + return &routesJSONClient{ + urlBase: urlBase(addr), + client: withoutRedirects(client), + } +} + +func (c *routesJSONClient) Get(ctx context.Context, in *GetRouteRequest) (*Route, error) { + url := c.urlBase + RoutesPathPrefix + "Get" + out := new(Route) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *routesJSONClient) GetAll(ctx context.Context, in *Nil) (*GetAllRoutesResponse, error) { + url := c.urlBase + RoutesPathPrefix + "GetAll" + out := new(GetAllRoutesResponse) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *routesJSONClient) Put(ctx context.Context, in *Route) (*Route, error) { + url := c.urlBase + RoutesPathPrefix + "Put" + out := new(Route) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *routesJSONClient) Delete(ctx context.Context, in *Route) (*Nil, error) { + url := c.urlBase + RoutesPathPrefix + "Delete" + out := new(Nil) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +// ===================== +// Routes Server Handler +// ===================== + +type routesServer struct { + Routes + hooks *twirp.ServerHooks +} + +func NewRoutesServer(svc Routes, hooks *twirp.ServerHooks) TwirpServer { + return &routesServer{ + Routes: svc, + hooks: hooks, + } +} + +// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func (s *routesServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// RoutesPathPrefix is used for all URL paths on a twirp Routes server. +// Requests are always: POST RoutesPathPrefix/method +// It can be used in an HTTP mux to route twirp requests along with non-twirp requests on other routes. +const RoutesPathPrefix = "/twirp/route.Routes/" + +func (s *routesServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "route") + ctx = ctxsetters.WithServiceName(ctx, "Routes") + ctx = ctxsetters.WithResponseWriter(ctx, resp) + + var err error + ctx, err = callRequestReceived(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + if req.Method != "POST" { + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) + err = badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, err) + return + } + + switch req.URL.Path { + case "/twirp/route.Routes/Get": + s.serveGet(ctx, resp, req) + return + case "/twirp/route.Routes/GetAll": + s.serveGetAll(ctx, resp, req) + return + case "/twirp/route.Routes/Put": + s.servePut(ctx, resp, req) + return + case "/twirp/route.Routes/Delete": + s.serveDelete(ctx, resp, req) + return + default: + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + err = badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, err) + return + } +} + +func (s *routesServer) serveGet(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveGetJSON(ctx, resp, req) + case "application/protobuf": + s.serveGetProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *routesServer) serveGetJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Get") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(GetRouteRequest) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Route + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Get(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Route and nil error while calling Get. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) serveGetProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Get") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(GetRouteRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Route + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Get(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Route and nil error while calling Get. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) serveGetAll(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveGetAllJSON(ctx, resp, req) + case "application/protobuf": + s.serveGetAllProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *routesServer) serveGetAllJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "GetAll") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(Nil) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *GetAllRoutesResponse + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.GetAll(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *GetAllRoutesResponse and nil error while calling GetAll. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) serveGetAllProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "GetAll") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(Nil) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *GetAllRoutesResponse + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.GetAll(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *GetAllRoutesResponse and nil error while calling GetAll. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) servePut(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.servePutJSON(ctx, resp, req) + case "application/protobuf": + s.servePutProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *routesServer) servePutJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Put") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(Route) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Route + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Put(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Route and nil error while calling Put. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) servePutProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Put") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(Route) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Route + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Put(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Route and nil error while calling Put. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) serveDelete(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveDeleteJSON(ctx, resp, req) + case "application/protobuf": + s.serveDeleteProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *routesServer) serveDeleteJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Delete") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(Route) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Delete(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Delete. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) serveDeleteProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Delete") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(Route) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Delete(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Delete. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *routesServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 0 +} + +func (s *routesServer) ProtocGenTwirpVersion() string { + return "v5.0.0" +} + +// ================ +// Tokens Interface +// ================ + +// Tokens lets a user manage the database authentication tokens. +type Tokens interface { + Get(context.Context, *GetTokenRequest) (*Token, error) + + GetAll(context.Context, *Nil) (*TokenSet, error) + + Put(context.Context, *Token) (*Token, error) + + Delete(context.Context, *Token) (*Nil, error) + + Deactivate(context.Context, *Token) (*Nil, error) +} + +// ====================== +// Tokens Protobuf Client +// ====================== + +type tokensProtobufClient struct { + urlBase string + client *http.Client +} + +// NewTokensProtobufClient creates a Protobuf client that implements the Tokens interface. +// It communicates using protobuf messages and can be configured with a custom http.Client. +func NewTokensProtobufClient(addr string, client *http.Client) Tokens { + return &tokensProtobufClient{ + urlBase: urlBase(addr), + client: withoutRedirects(client), + } +} + +func (c *tokensProtobufClient) Get(ctx context.Context, in *GetTokenRequest) (*Token, error) { + url := c.urlBase + TokensPathPrefix + "Get" + out := new(Token) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensProtobufClient) GetAll(ctx context.Context, in *Nil) (*TokenSet, error) { + url := c.urlBase + TokensPathPrefix + "GetAll" + out := new(TokenSet) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensProtobufClient) Put(ctx context.Context, in *Token) (*Token, error) { + url := c.urlBase + TokensPathPrefix + "Put" + out := new(Token) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensProtobufClient) Delete(ctx context.Context, in *Token) (*Nil, error) { + url := c.urlBase + TokensPathPrefix + "Delete" + out := new(Nil) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensProtobufClient) Deactivate(ctx context.Context, in *Token) (*Nil, error) { + url := c.urlBase + TokensPathPrefix + "Deactivate" + out := new(Nil) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +// ================== +// Tokens JSON Client +// ================== + +type tokensJSONClient struct { + urlBase string + client *http.Client +} + +// NewTokensJSONClient creates a JSON client that implements the Tokens interface. +// It communicates using JSON requests and responses instead of protobuf messages. +func NewTokensJSONClient(addr string, client *http.Client) Tokens { + return &tokensJSONClient{ + urlBase: urlBase(addr), + client: withoutRedirects(client), + } +} + +func (c *tokensJSONClient) Get(ctx context.Context, in *GetTokenRequest) (*Token, error) { + url := c.urlBase + TokensPathPrefix + "Get" + out := new(Token) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensJSONClient) GetAll(ctx context.Context, in *Nil) (*TokenSet, error) { + url := c.urlBase + TokensPathPrefix + "GetAll" + out := new(TokenSet) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensJSONClient) Put(ctx context.Context, in *Token) (*Token, error) { + url := c.urlBase + TokensPathPrefix + "Put" + out := new(Token) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensJSONClient) Delete(ctx context.Context, in *Token) (*Nil, error) { + url := c.urlBase + TokensPathPrefix + "Delete" + out := new(Nil) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *tokensJSONClient) Deactivate(ctx context.Context, in *Token) (*Nil, error) { + url := c.urlBase + TokensPathPrefix + "Deactivate" + out := new(Nil) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +// ===================== +// Tokens Server Handler +// ===================== + +type tokensServer struct { + Tokens + hooks *twirp.ServerHooks +} + +func NewTokensServer(svc Tokens, hooks *twirp.ServerHooks) TwirpServer { + return &tokensServer{ + Tokens: svc, + hooks: hooks, + } +} + +// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func (s *tokensServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// TokensPathPrefix is used for all URL paths on a twirp Tokens server. +// Requests are always: POST TokensPathPrefix/method +// It can be used in an HTTP mux to route twirp requests along with non-twirp requests on other routes. +const TokensPathPrefix = "/twirp/route.Tokens/" + +func (s *tokensServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "route") + ctx = ctxsetters.WithServiceName(ctx, "Tokens") + ctx = ctxsetters.WithResponseWriter(ctx, resp) + + var err error + ctx, err = callRequestReceived(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + if req.Method != "POST" { + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) + err = badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, err) + return + } + + switch req.URL.Path { + case "/twirp/route.Tokens/Get": + s.serveGet(ctx, resp, req) + return + case "/twirp/route.Tokens/GetAll": + s.serveGetAll(ctx, resp, req) + return + case "/twirp/route.Tokens/Put": + s.servePut(ctx, resp, req) + return + case "/twirp/route.Tokens/Delete": + s.serveDelete(ctx, resp, req) + return + case "/twirp/route.Tokens/Deactivate": + s.serveDeactivate(ctx, resp, req) + return + default: + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + err = badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, err) + return + } +} + +func (s *tokensServer) serveGet(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveGetJSON(ctx, resp, req) + case "application/protobuf": + s.serveGetProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *tokensServer) serveGetJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Get") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(GetTokenRequest) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Token + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Get(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Token and nil error while calling Get. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) serveGetProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Get") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(GetTokenRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Token + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Get(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Token and nil error while calling Get. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) serveGetAll(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveGetAllJSON(ctx, resp, req) + case "application/protobuf": + s.serveGetAllProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *tokensServer) serveGetAllJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "GetAll") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(Nil) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *TokenSet + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.GetAll(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *TokenSet and nil error while calling GetAll. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) serveGetAllProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "GetAll") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(Nil) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *TokenSet + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.GetAll(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *TokenSet and nil error while calling GetAll. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) servePut(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.servePutJSON(ctx, resp, req) + case "application/protobuf": + s.servePutProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *tokensServer) servePutJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Put") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(Token) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Token + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Put(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Token and nil error while calling Put. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) servePutProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Put") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(Token) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Token + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Put(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Token and nil error while calling Put. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) serveDelete(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveDeleteJSON(ctx, resp, req) + case "application/protobuf": + s.serveDeleteProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *tokensServer) serveDeleteJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Delete") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(Token) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Delete(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Delete. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) serveDeleteProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Delete") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(Token) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Delete(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Delete. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) serveDeactivate(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveDeactivateJSON(ctx, resp, req) + case "application/protobuf": + s.serveDeactivateProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *tokensServer) serveDeactivateJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Deactivate") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(Token) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Deactivate(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Deactivate. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) serveDeactivateProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Deactivate") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(Token) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Deactivate(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Deactivate. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *tokensServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 1 +} + +func (s *tokensServer) ProtocGenTwirpVersion() string { + return "v5.0.0" +} + +// ================== +// Backends Interface +// ================== + +type Backends interface { + List(context.Context, *BackendSelector) (*BackendList, error) + + Kill(context.Context, *BackendID) (*Nil, error) +} + +// ======================== +// Backends Protobuf Client +// ======================== + +type backendsProtobufClient struct { + urlBase string + client *http.Client +} + +// NewBackendsProtobufClient creates a Protobuf client that implements the Backends interface. +// It communicates using protobuf messages and can be configured with a custom http.Client. +func NewBackendsProtobufClient(addr string, client *http.Client) Backends { + return &backendsProtobufClient{ + urlBase: urlBase(addr), + client: withoutRedirects(client), + } +} + +func (c *backendsProtobufClient) List(ctx context.Context, in *BackendSelector) (*BackendList, error) { + url := c.urlBase + BackendsPathPrefix + "List" + out := new(BackendList) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *backendsProtobufClient) Kill(ctx context.Context, in *BackendID) (*Nil, error) { + url := c.urlBase + BackendsPathPrefix + "Kill" + out := new(Nil) + err := doProtoRequest(ctx, c.client, url, in, out) + return out, err +} + +// ==================== +// Backends JSON Client +// ==================== + +type backendsJSONClient struct { + urlBase string + client *http.Client +} + +// NewBackendsJSONClient creates a JSON client that implements the Backends interface. +// It communicates using JSON requests and responses instead of protobuf messages. +func NewBackendsJSONClient(addr string, client *http.Client) Backends { + return &backendsJSONClient{ + urlBase: urlBase(addr), + client: withoutRedirects(client), + } +} + +func (c *backendsJSONClient) List(ctx context.Context, in *BackendSelector) (*BackendList, error) { + url := c.urlBase + BackendsPathPrefix + "List" + out := new(BackendList) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +func (c *backendsJSONClient) Kill(ctx context.Context, in *BackendID) (*Nil, error) { + url := c.urlBase + BackendsPathPrefix + "Kill" + out := new(Nil) + err := doJSONRequest(ctx, c.client, url, in, out) + return out, err +} + +// ======================= +// Backends Server Handler +// ======================= + +type backendsServer struct { + Backends + hooks *twirp.ServerHooks +} + +func NewBackendsServer(svc Backends, hooks *twirp.ServerHooks) TwirpServer { + return &backendsServer{ + Backends: svc, + hooks: hooks, + } +} + +// writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func (s *backendsServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { + writeError(ctx, resp, err, s.hooks) +} + +// BackendsPathPrefix is used for all URL paths on a twirp Backends server. +// Requests are always: POST BackendsPathPrefix/method +// It can be used in an HTTP mux to route twirp requests along with non-twirp requests on other routes. +const BackendsPathPrefix = "/twirp/route.Backends/" + +func (s *backendsServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { + ctx := req.Context() + ctx = ctxsetters.WithPackageName(ctx, "route") + ctx = ctxsetters.WithServiceName(ctx, "Backends") + ctx = ctxsetters.WithResponseWriter(ctx, resp) + + var err error + ctx, err = callRequestReceived(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + if req.Method != "POST" { + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) + err = badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, err) + return + } + + switch req.URL.Path { + case "/twirp/route.Backends/List": + s.serveList(ctx, resp, req) + return + case "/twirp/route.Backends/Kill": + s.serveKill(ctx, resp, req) + return + default: + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) + err = badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, err) + return + } +} + +func (s *backendsServer) serveList(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveListJSON(ctx, resp, req) + case "application/protobuf": + s.serveListProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *backendsServer) serveListJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "List") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(BackendSelector) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *BackendList + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.List(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *BackendList and nil error while calling List. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *backendsServer) serveListProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "List") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(BackendSelector) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *BackendList + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.List(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *BackendList and nil error while calling List. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *backendsServer) serveKill(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + switch req.Header.Get("Content-Type") { + case "application/json": + s.serveKillJSON(ctx, resp, req) + case "application/protobuf": + s.serveKillProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *backendsServer) serveKillJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Kill") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + reqContent := new(BackendID) + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil { + err = wrapErr(err, "failed to parse request json") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Kill(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Kill. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + var buf bytes.Buffer + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(&buf, respContent); err != nil { + err = wrapErr(err, "failed to marshal json response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(buf.Bytes()); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *backendsServer) serveKillProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "Kill") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + defer closebody(req.Body) + buf, err := ioutil.ReadAll(req.Body) + if err != nil { + err = wrapErr(err, "failed to read request body") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + reqContent := new(BackendID) + if err = proto.Unmarshal(buf, reqContent); err != nil { + err = wrapErr(err, "failed to parse request proto") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + // Call service method + var respContent *Nil + func() { + defer func() { + // In case of a panic, serve a 500 error and then panic. + if r := recover(); r != nil { + s.writeError(ctx, resp, twirp.InternalError("Internal service panic")) + panic(r) + } + }() + respContent, err = s.Kill(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *Nil and nil error while calling Kill. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + err = wrapErr(err, "failed to marshal proto response") + s.writeError(ctx, resp, twirp.InternalErrorWith(err)) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.WriteHeader(http.StatusOK) + if _, err = resp.Write(respBytes); err != nil { + log.Printf("errored while writing response to client, but already sent response status code to 200: %s", err) + } + callResponseSent(ctx, s.hooks) +} + +func (s *backendsServer) ServiceDescriptor() ([]byte, int) { + return twirpFileDescriptor0, 2 +} + +func (s *backendsServer) ProtocGenTwirpVersion() string { + return "v5.0.0" +} + +// ===== +// Utils +// ===== + +// TwirpServer is the interface generated server structs will support: they're +// HTTP handlers with additional methods for accessing metadata about the +// service. Those accessors are a low-level API for building reflection tools. +// Most people can think of TwirpServers as just http.Handlers. +type TwirpServer interface { + http.Handler + // ServiceDescriptor returns gzipped bytes describing the .proto file that + // this service was generated from. Once unzipped, the bytes can be + // unmarshaled as a + // github.com/golang/protobuf/protoc-gen-go/descriptor.FileDescriptorProto. + // + // The returned integer is the index of this particular service within that + // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a + // low-level field, expected to be used for reflection. + ServiceDescriptor() ([]byte, int) + // ProtocGenTwirpVersion is the semantic version string of the version of + // twirp used to generate this file. + ProtocGenTwirpVersion() string +} + +// done returns ctx.Err() if ctx.Done() indicates that the context done +func done(ctx context.Context) error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + return nil + } +} + +// WriteError writes an HTTP response with a valid Twirp error format. +// If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) +func WriteError(resp http.ResponseWriter, err error) { + writeError(context.Background(), resp, err, nil) +} + +// writeError writes Twirp errors in the response and triggers hooks. +func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) { + // Non-twirp errors are wrapped as Internal (default) + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + + statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code()) + ctx = ctxsetters.WithStatusCode(ctx, statusCode) + ctx = callError(ctx, hooks, twerr) + + resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON (instead of protobuf) + resp.WriteHeader(statusCode) // HTTP response status code + + respBody := marshalErrorToJSON(twerr) + _, err2 := resp.Write(respBody) + if err2 != nil { + log.Printf("unable to send error message %q: %s", twerr, err2) + } + + callResponseSent(ctx, hooks) +} + +// urlBase helps ensure that addr specifies a scheme. If it is unparsable +// as a URL, it returns addr unchanged. +func urlBase(addr string) string { + // If the addr specifies a scheme, use it. If not, default to + // http. If url.Parse fails on it, return it unchanged. + url, err := url.Parse(addr) + if err != nil { + return addr + } + if url.Scheme == "" { + url.Scheme = "http" + } + return url.String() +} + +// getCustomHTTPReqHeaders retrieves a copy of any headers that are set in +// a context through the twirp.WithHTTPRequestHeaders function. +// If there are no headers set, or if they have the wrong type, nil is returned. +func getCustomHTTPReqHeaders(ctx context.Context) http.Header { + header, ok := twirp.HTTPRequestHeaders(ctx) + if !ok || header == nil { + return nil + } + copied := make(http.Header) + for k, vv := range header { + if vv == nil { + copied[k] = nil + continue + } + copied[k] = make([]string, len(vv)) + copy(copied[k], vv) + } + return copied +} + +// closebody closes a response or request body and just logs +// any error encountered while closing, since errors are +// considered very unusual. +func closebody(body io.Closer) { + if err := body.Close(); err != nil { + log.Printf("error closing body: %q", err) + } +} + +// newRequest makes an http.Request from a client, adding common headers. +func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) { + req, err := http.NewRequest("POST", url, reqBody) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil { + req.Header = customHeader + } + req.Header.Set("Content-Type", contentType) + req.Header.Set("Twirp-Version", "v5.0.0") + return req, nil +} + +// JSON serialization for errors +type twerrJSON struct { + Code string `json:"code"` + Msg string `json:"msg"` + Meta map[string]string `json:"meta,omitempty"` +} + +// marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body. +// If serialization fails, it will use a descriptive Internal error instead. +func marshalErrorToJSON(twerr twirp.Error) []byte { + // make sure that msg is not too large + msg := twerr.Msg() + if len(msg) > 1e6 { + msg = msg[:1e6] + } + + tj := twerrJSON{ + Code: string(twerr.Code()), + Msg: msg, + Meta: twerr.MetaMap(), + } + + buf, err := json.Marshal(&tj) + if err != nil { + buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback + } + + return buf +} + +// errorFromResponse builds a twirp.Error from a non-200 HTTP response. +// If the response has a valid serialized Twirp error, then it's returned. +// If not, the response status code is used to generate a similar twirp +// error. See twirpErrorFromIntermediary for more info on intermediary errors. +func errorFromResponse(resp *http.Response) twirp.Error { + statusCode := resp.StatusCode + statusText := http.StatusText(statusCode) + + if isHTTPRedirect(statusCode) { + // Unexpected redirect: it must be an error from an intermediary. + // Twirp clients dont't follow redirects automatically, Twirp only handles + // POST requests, redirects should only happen on GET and HEAD requests. + location := resp.Header.Get("Location") + msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location) + return twirpErrorFromIntermediary(statusCode, msg, location) + } + + respBodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return clientError("failed to read server error response body", err) + } + var tj twerrJSON + if err := json.Unmarshal(respBodyBytes, &tj); err != nil { + // Invalid JSON response; it must be an error from an intermediary. + msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText) + return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes)) + } + + errorCode := twirp.ErrorCode(tj.Code) + if !twirp.IsValidErrorCode(errorCode) { + msg := "invalid type returned from server error response: " + tj.Code + return twirp.InternalError(msg) + } + + twerr := twirp.NewError(errorCode, tj.Msg) + for k, v := range tj.Meta { + twerr = twerr.WithMeta(k, v) + } + return twerr +} + +// twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors. +// The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. +// Returned twirp Errors have some additional metadata for inspection. +func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error { + var code twirp.ErrorCode + if isHTTPRedirect(status) { // 3xx + code = twirp.Internal + } else { + switch status { + case 400: // Bad Request + code = twirp.Internal + case 401: // Unauthorized + code = twirp.Unauthenticated + case 403: // Forbidden + code = twirp.PermissionDenied + case 404: // Not Found + code = twirp.BadRoute + case 429, 502, 503, 504: // Too Many Requests, Bad Gateway, Service Unavailable, Gateway Timeout + code = twirp.Unavailable + default: // All other codes + code = twirp.Unknown + } + } + + twerr := twirp.NewError(code, msg) + twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary + twerr = twerr.WithMeta("status_code", strconv.Itoa(status)) + if isHTTPRedirect(status) { + twerr = twerr.WithMeta("location", bodyOrLocation) + } else { + twerr = twerr.WithMeta("body", bodyOrLocation) + } + return twerr +} +func isHTTPRedirect(status int) bool { + return status >= 300 && status <= 399 +} + +// wrappedError implements the github.com/pkg/errors.Causer interface, allowing errors to be +// examined for their root cause. +type wrappedError struct { + msg string + cause error +} + +func wrapErr(err error, msg string) error { return &wrappedError{msg: msg, cause: err} } +func (e *wrappedError) Cause() error { return e.cause } +func (e *wrappedError) Error() string { return e.msg + ": " + e.cause.Error() } + +// clientError adds consistency to errors generated in the client +func clientError(desc string, err error) twirp.Error { + return twirp.InternalErrorWith(wrapErr(err, desc)) +} + +// badRouteError is used when the twirp server cannot route a request +func badRouteError(msg string, method, url string) twirp.Error { + err := twirp.NewError(twirp.BadRoute, msg) + err = err.WithMeta("twirp_invalid_route", method+" "+url) + return err +} + +// The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or +// 303 response, and also 301s in go1.8. It redirects by making a second request, changing the +// method to GET and removing the body. This produces very confusing error messages, so instead we +// set a redirect policy that always errors. This stops Go from executing the redirect. +// +// We have to be a little careful in case the user-provided http.Client has its own CheckRedirect +// policy - if so, we'll run through that policy first. +// +// Because this requires modifying the http.Client, we make a new copy of the client and return it. +func withoutRedirects(in *http.Client) *http.Client { + copy := *in + copy.CheckRedirect = func(req *http.Request, via []*http.Request) error { + if in.CheckRedirect != nil { + // Run the input's redirect if it exists, in case it has side effects, but ignore any error it + // returns, since we want to use ErrUseLastResponse. + err := in.CheckRedirect(req, via) + _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use. + } + return http.ErrUseLastResponse + } + return © +} + +// doProtoRequest is common code to make a request to the remote twirp service. +func doProtoRequest(ctx context.Context, client *http.Client, url string, in, out proto.Message) error { + var err error + reqBodyBytes, err := proto.Marshal(in) + if err != nil { + return clientError("failed to marshal proto request", err) + } + reqBody := bytes.NewBuffer(reqBodyBytes) + if err = done(ctx); err != nil { + return clientError("aborted because context was done", err) + } + + req, err := newRequest(ctx, url, reqBody, "application/protobuf") + if err != nil { + return clientError("could not build request", err) + } + resp, err := client.Do(req) + if err != nil { + return clientError("failed to do request", err) + } + defer closebody(resp.Body) + if err = done(ctx); err != nil { + return clientError("aborted because context was done", err) + } + + if resp.StatusCode != 200 { + return errorFromResponse(resp) + } + + respBodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return clientError("failed to read response body", err) + } + if err = done(ctx); err != nil { + return clientError("aborted because context was done", err) + } + + if err = proto.Unmarshal(respBodyBytes, out); err != nil { + return clientError("failed to unmarshal proto response", err) + } + return nil +} + +// doJSONRequest is common code to make a request to the remote twirp service. +func doJSONRequest(ctx context.Context, client *http.Client, url string, in, out proto.Message) error { + var err error + reqBody := bytes.NewBuffer(nil) + marshaler := &jsonpb.Marshaler{OrigName: true} + if err = marshaler.Marshal(reqBody, in); err != nil { + return clientError("failed to marshal json request", err) + } + if err = done(ctx); err != nil { + return clientError("aborted because context was done", err) + } + + req, err := newRequest(ctx, url, reqBody, "application/json") + if err != nil { + return clientError("could not build request", err) + } + resp, err := client.Do(req) + if err != nil { + return clientError("failed to do request", err) + } + defer closebody(resp.Body) + if err = done(ctx); err != nil { + return clientError("aborted because context was done", err) + } + + if resp.StatusCode != 200 { + return errorFromResponse(resp) + } + + unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true} + if err = unmarshaler.Unmarshal(resp.Body, out); err != nil { + return clientError("failed to unmarshal json response", err) + } + if err = done(ctx); err != nil { + return clientError("aborted because context was done", err) + } + return nil +} + +// Call twirp.ServerHooks.RequestReceived if the hook is available +func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { + if h == nil || h.RequestReceived == nil { + return ctx, nil + } + return h.RequestReceived(ctx) +} + +// Call twirp.ServerHooks.RequestRouted if the hook is available +func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) { + if h == nil || h.RequestRouted == nil { + return ctx, nil + } + return h.RequestRouted(ctx) +} + +// Call twirp.ServerHooks.ResponsePrepared if the hook is available +func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context { + if h == nil || h.ResponsePrepared == nil { + return ctx + } + return h.ResponsePrepared(ctx) +} + +// Call twirp.ServerHooks.ResponseSent if the hook is available +func callResponseSent(ctx context.Context, h *twirp.ServerHooks) { + if h == nil || h.ResponseSent == nil { + return + } + h.ResponseSent(ctx) +} + +// Call twirp.ServerHooks.Error if the hook is available +func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context { + if h == nil || h.Error == nil { + return ctx + } + return h.Error(ctx, err) +} + +var twirpFileDescriptor0 = []byte{ + // 556 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xd1, 0x6a, 0xdb, 0x4a, + 0x10, 0xb5, 0x24, 0x4b, 0xb1, 0xc7, 0x97, 0x38, 0x0c, 0x26, 0x08, 0xe7, 0xc5, 0xec, 0x0d, 0xa9, + 0x29, 0x34, 0x14, 0xa7, 0x50, 0x0a, 0xed, 0x43, 0x8b, 0x8b, 0x09, 0x2d, 0xa1, 0xc8, 0x7d, 0xeb, + 0x93, 0x64, 0x0d, 0x58, 0x44, 0xf5, 0xba, 0xda, 0x55, 0xa1, 0x9f, 0xd2, 0x2f, 0xe9, 0x97, 0xf4, + 0x7f, 0xca, 0xce, 0xae, 0x63, 0xcb, 0x4a, 0xfb, 0x36, 0x33, 0x67, 0x76, 0x74, 0xce, 0xcc, 0x11, + 0x0c, 0x2a, 0x59, 0x6b, 0xba, 0xde, 0x56, 0x52, 0x4b, 0x0c, 0x39, 0x11, 0x21, 0x04, 0x77, 0x45, + 0x29, 0x5e, 0xc1, 0x70, 0x41, 0x3a, 0x31, 0xa5, 0x84, 0xbe, 0xd5, 0xa4, 0x34, 0x9e, 0x43, 0x54, + 0x6f, 0x6a, 0x45, 0x79, 0xec, 0x4d, 0xbc, 0x69, 0x3f, 0x71, 0x19, 0x9e, 0x82, 0x5f, 0xe4, 0xb1, + 0xcf, 0x35, 0xbf, 0xc8, 0xc5, 0x7b, 0x08, 0xf9, 0x9d, 0x03, 0xbc, 0x1d, 0x80, 0x31, 0x9c, 0xac, + 0x2a, 0x4a, 0xb5, 0xac, 0x5c, 0xf7, 0x2e, 0x45, 0x84, 0xee, 0x5a, 0x2a, 0x1d, 0x07, 0x5c, 0xe6, + 0x58, 0xbc, 0x86, 0xd1, 0x82, 0xf4, 0xdb, 0xb2, 0xe4, 0x61, 0x2a, 0x21, 0xb5, 0x95, 0x1b, 0x45, + 0x78, 0x09, 0x11, 0x33, 0x55, 0xb1, 0x37, 0x09, 0xa6, 0x83, 0xd9, 0x7f, 0xd7, 0x56, 0x85, 0xe5, + 0xea, 0x30, 0xf1, 0x05, 0xc2, 0xcf, 0xf2, 0x9e, 0x36, 0x2d, 0x12, 0x08, 0xdd, 0x4c, 0xe6, 0x3f, + 0x1c, 0x03, 0x8e, 0x8d, 0x32, 0xb5, 0x92, 0x5b, 0x52, 0x71, 0x30, 0x09, 0x8c, 0x32, 0x9b, 0x99, + 0x7a, 0xba, 0xd2, 0xc5, 0x77, 0x8a, 0xbb, 0x13, 0x6f, 0xda, 0x4b, 0x5c, 0x26, 0x9e, 0x43, 0x8f, + 0x87, 0x2f, 0x49, 0x1b, 0x3a, 0xda, 0xc4, 0xc7, 0x74, 0xb8, 0x21, 0x71, 0x98, 0x78, 0xc9, 0xeb, + 0xb4, 0x35, 0xb7, 0xce, 0x11, 0x84, 0x0c, 0x3a, 0x6e, 0x36, 0x69, 0x2d, 0xf3, 0xa7, 0x07, 0x27, + 0xef, 0xd2, 0xd5, 0x3d, 0x6d, 0xf2, 0x96, 0x94, 0x11, 0x84, 0x7c, 0x3a, 0xd7, 0x6e, 0x13, 0x23, + 0xb0, 0x56, 0x54, 0xed, 0x76, 0x69, 0x62, 0x23, 0x24, 0x97, 0x5f, 0xd3, 0x62, 0xc3, 0x42, 0xfa, + 0x89, 0xcb, 0xf0, 0x0c, 0x82, 0xed, 0xba, 0x88, 0xc3, 0x89, 0x37, 0xf5, 0x13, 0x13, 0x3e, 0x5c, + 0x22, 0xda, 0x5f, 0x82, 0x0f, 0xaf, 0xd2, 0xac, 0xa4, 0xf8, 0xc4, 0xae, 0xc1, 0x66, 0x22, 0x85, + 0x81, 0xa3, 0xf6, 0xb1, 0x50, 0x1a, 0xaf, 0xc0, 0xcf, 0x14, 0xd3, 0x1b, 0xcc, 0xce, 0xdd, 0x16, + 0x1c, 0xbe, 0xa4, 0x92, 0x56, 0x5a, 0x56, 0x89, 0x9f, 0x29, 0x7c, 0x0a, 0xbd, 0xcc, 0x96, 0x55, + 0xec, 0xf3, 0xce, 0x4e, 0x9b, 0xdd, 0xc9, 0x03, 0x2e, 0xde, 0xc0, 0xf0, 0x68, 0xc4, 0x81, 0x16, + 0xaf, 0xa1, 0x65, 0xa7, 0xdb, 0xdf, 0xeb, 0x16, 0x17, 0xd0, 0x77, 0xcf, 0x6f, 0xe7, 0xc7, 0xeb, + 0x9b, 0xfd, 0xf2, 0x20, 0xb2, 0xde, 0xc2, 0x67, 0x10, 0x2c, 0x48, 0xe3, 0x8e, 0xf5, 0x91, 0xf3, + 0xc7, 0x0d, 0x8b, 0x89, 0x0e, 0xde, 0x40, 0x64, 0xad, 0x89, 0xe0, 0x90, 0xbb, 0xa2, 0x1c, 0x5f, + 0xec, 0x5f, 0xb7, 0x5c, 0x2b, 0x3a, 0xf8, 0x3f, 0x04, 0x9f, 0x6a, 0x8d, 0x8d, 0x59, 0xad, 0xc9, + 0x97, 0x10, 0xcd, 0xa9, 0x24, 0x4d, 0x47, 0x7d, 0x07, 0xdf, 0x11, 0x9d, 0xd9, 0x6f, 0x0f, 0x22, + 0xf6, 0xd2, 0x63, 0xcc, 0x0f, 0x4d, 0x36, 0x6e, 0xb8, 0x51, 0x74, 0xf0, 0xc9, 0xa3, 0xcc, 0x87, + 0x87, 0x5d, 0x4b, 0xd2, 0x6d, 0xb6, 0x8c, 0xb4, 0xa6, 0xb5, 0xd9, 0xda, 0xbe, 0x06, 0x5b, 0x9c, + 0x02, 0xcc, 0x89, 0xff, 0x9c, 0xf4, 0xdf, 0x9d, 0xb3, 0x35, 0xf4, 0xdc, 0xb9, 0x14, 0xbe, 0x80, + 0x2e, 0xbb, 0xea, 0x2f, 0x4e, 0x1a, 0x63, 0xb3, 0x6e, 0x7a, 0x45, 0x07, 0xaf, 0xa0, 0xfb, 0xa1, + 0x28, 0x4b, 0x3c, 0x6b, 0xa2, 0xb7, 0xf3, 0xe6, 0x97, 0xb2, 0x88, 0xff, 0x95, 0x9b, 0x3f, 0x01, + 0x00, 0x00, 0xff, 0xff, 0x48, 0xf0, 0xaf, 0x88, 0x02, 0x05, 0x00, 0x00, +} diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go new file mode 100644 index 0000000..dfdfc5b --- /dev/null +++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go @@ -0,0 +1,1082 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2015 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* +Package jsonpb provides marshaling and unmarshaling between protocol buffers and JSON. +It follows the specification at https://developers.google.com/protocol-buffers/docs/proto3#json. + +This package produces a different output than the standard "encoding/json" package, +which does not operate correctly on protocol buffers. +*/ +package jsonpb + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "github.com/golang/protobuf/proto" + + stpb "github.com/golang/protobuf/ptypes/struct" +) + +// Marshaler is a configurable object for converting between +// protocol buffer objects and a JSON representation for them. +type Marshaler struct { + // Whether to render enum values as integers, as opposed to string values. + EnumsAsInts bool + + // Whether to render fields with zero values. + EmitDefaults bool + + // A string to indent each level by. The presence of this field will + // also cause a space to appear between the field separator and + // value, and for newlines to be appear between fields and array + // elements. + Indent string + + // Whether to use the original (.proto) name for fields. + OrigName bool + + // A custom URL resolver to use when marshaling Any messages to JSON. + // If unset, the default resolution strategy is to extract the + // fully-qualified type name from the type URL and pass that to + // proto.MessageType(string). + AnyResolver AnyResolver +} + +// AnyResolver takes a type URL, present in an Any message, and resolves it into +// an instance of the associated message. +type AnyResolver interface { + Resolve(typeUrl string) (proto.Message, error) +} + +func defaultResolveAny(typeUrl string) (proto.Message, error) { + // Only the part of typeUrl after the last slash is relevant. + mname := typeUrl + if slash := strings.LastIndex(mname, "/"); slash >= 0 { + mname = mname[slash+1:] + } + mt := proto.MessageType(mname) + if mt == nil { + return nil, fmt.Errorf("unknown message type %q", mname) + } + return reflect.New(mt.Elem()).Interface().(proto.Message), nil +} + +// JSONPBMarshaler is implemented by protobuf messages that customize the +// way they are marshaled to JSON. Messages that implement this should +// also implement JSONPBUnmarshaler so that the custom format can be +// parsed. +type JSONPBMarshaler interface { + MarshalJSONPB(*Marshaler) ([]byte, error) +} + +// JSONPBUnmarshaler is implemented by protobuf messages that customize +// the way they are unmarshaled from JSON. Messages that implement this +// should also implement JSONPBMarshaler so that the custom format can be +// produced. +type JSONPBUnmarshaler interface { + UnmarshalJSONPB(*Unmarshaler, []byte) error +} + +// Marshal marshals a protocol buffer into JSON. +func (m *Marshaler) Marshal(out io.Writer, pb proto.Message) error { + writer := &errWriter{writer: out} + return m.marshalObject(writer, pb, "", "") +} + +// MarshalToString converts a protocol buffer object to JSON string. +func (m *Marshaler) MarshalToString(pb proto.Message) (string, error) { + var buf bytes.Buffer + if err := m.Marshal(&buf, pb); err != nil { + return "", err + } + return buf.String(), nil +} + +type int32Slice []int32 + +var nonFinite = map[string]float64{ + `"NaN"`: math.NaN(), + `"Infinity"`: math.Inf(1), + `"-Infinity"`: math.Inf(-1), +} + +// For sorting extensions ids to ensure stable output. +func (s int32Slice) Len() int { return len(s) } +func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type wkt interface { + XXX_WellKnownType() string +} + +// marshalObject writes a struct to the Writer. +func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent, typeURL string) error { + if jsm, ok := v.(JSONPBMarshaler); ok { + b, err := jsm.MarshalJSONPB(m) + if err != nil { + return err + } + if typeURL != "" { + // we are marshaling this object to an Any type + var js map[string]*json.RawMessage + if err = json.Unmarshal(b, &js); err != nil { + return fmt.Errorf("type %T produced invalid JSON: %v", v, err) + } + turl, err := json.Marshal(typeURL) + if err != nil { + return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err) + } + js["@type"] = (*json.RawMessage)(&turl) + if b, err = json.Marshal(js); err != nil { + return err + } + } + + out.write(string(b)) + return out.err + } + + s := reflect.ValueOf(v).Elem() + + // Handle well-known types. + if wkt, ok := v.(wkt); ok { + switch wkt.XXX_WellKnownType() { + case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value", + "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue": + // "Wrappers use the same representation in JSON + // as the wrapped primitive type, ..." + sprop := proto.GetProperties(s.Type()) + return m.marshalValue(out, sprop.Prop[0], s.Field(0), indent) + case "Any": + // Any is a bit more involved. + return m.marshalAny(out, v, indent) + case "Duration": + // "Generated output always contains 3, 6, or 9 fractional digits, + // depending on required precision." + s, ns := s.Field(0).Int(), s.Field(1).Int() + x := fmt.Sprintf("%d.%09d", s, ns) + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + out.write(`"`) + out.write(x) + out.write(`s"`) + return out.err + case "Struct", "ListValue": + // Let marshalValue handle the `Struct.fields` map or the `ListValue.values` slice. + // TODO: pass the correct Properties if needed. + return m.marshalValue(out, &proto.Properties{}, s.Field(0), indent) + case "Timestamp": + // "RFC 3339, where generated output will always be Z-normalized + // and uses 3, 6 or 9 fractional digits." + s, ns := s.Field(0).Int(), s.Field(1).Int() + t := time.Unix(s, ns).UTC() + // time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits). + x := t.Format("2006-01-02T15:04:05.000000000") + x = strings.TrimSuffix(x, "000") + x = strings.TrimSuffix(x, "000") + out.write(`"`) + out.write(x) + out.write(`Z"`) + return out.err + case "Value": + // Value has a single oneof. + kind := s.Field(0) + if kind.IsNil() { + // "absence of any variant indicates an error" + return errors.New("nil Value") + } + // oneof -> *T -> T -> T.F + x := kind.Elem().Elem().Field(0) + // TODO: pass the correct Properties if needed. + return m.marshalValue(out, &proto.Properties{}, x, indent) + } + } + + out.write("{") + if m.Indent != "" { + out.write("\n") + } + + firstField := true + + if typeURL != "" { + if err := m.marshalTypeURL(out, indent, typeURL); err != nil { + return err + } + firstField = false + } + + for i := 0; i < s.NumField(); i++ { + value := s.Field(i) + valueField := s.Type().Field(i) + if strings.HasPrefix(valueField.Name, "XXX_") { + continue + } + + // IsNil will panic on most value kinds. + switch value.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface: + if value.IsNil() { + continue + } + } + + if !m.EmitDefaults { + switch value.Kind() { + case reflect.Bool: + if !value.Bool() { + continue + } + case reflect.Int32, reflect.Int64: + if value.Int() == 0 { + continue + } + case reflect.Uint32, reflect.Uint64: + if value.Uint() == 0 { + continue + } + case reflect.Float32, reflect.Float64: + if value.Float() == 0 { + continue + } + case reflect.String: + if value.Len() == 0 { + continue + } + case reflect.Map, reflect.Ptr, reflect.Slice: + if value.IsNil() { + continue + } + } + } + + // Oneof fields need special handling. + if valueField.Tag.Get("protobuf_oneof") != "" { + // value is an interface containing &T{real_value}. + sv := value.Elem().Elem() // interface -> *T -> T + value = sv.Field(0) + valueField = sv.Type().Field(0) + } + prop := jsonProperties(valueField, m.OrigName) + if !firstField { + m.writeSep(out) + } + if err := m.marshalField(out, prop, value, indent); err != nil { + return err + } + firstField = false + } + + // Handle proto2 extensions. + if ep, ok := v.(proto.Message); ok { + extensions := proto.RegisteredExtensions(v) + // Sort extensions for stable output. + ids := make([]int32, 0, len(extensions)) + for id, desc := range extensions { + if !proto.HasExtension(ep, desc) { + continue + } + ids = append(ids, id) + } + sort.Sort(int32Slice(ids)) + for _, id := range ids { + desc := extensions[id] + if desc == nil { + // unknown extension + continue + } + ext, extErr := proto.GetExtension(ep, desc) + if extErr != nil { + return extErr + } + value := reflect.ValueOf(ext) + var prop proto.Properties + prop.Parse(desc.Tag) + prop.JSONName = fmt.Sprintf("[%s]", desc.Name) + if !firstField { + m.writeSep(out) + } + if err := m.marshalField(out, &prop, value, indent); err != nil { + return err + } + firstField = false + } + + } + + if m.Indent != "" { + out.write("\n") + out.write(indent) + } + out.write("}") + return out.err +} + +func (m *Marshaler) writeSep(out *errWriter) { + if m.Indent != "" { + out.write(",\n") + } else { + out.write(",") + } +} + +func (m *Marshaler) marshalAny(out *errWriter, any proto.Message, indent string) error { + // "If the Any contains a value that has a special JSON mapping, + // it will be converted as follows: {"@type": xxx, "value": yyy}. + // Otherwise, the value will be converted into a JSON object, + // and the "@type" field will be inserted to indicate the actual data type." + v := reflect.ValueOf(any).Elem() + turl := v.Field(0).String() + val := v.Field(1).Bytes() + + var msg proto.Message + var err error + if m.AnyResolver != nil { + msg, err = m.AnyResolver.Resolve(turl) + } else { + msg, err = defaultResolveAny(turl) + } + if err != nil { + return err + } + + if err := proto.Unmarshal(val, msg); err != nil { + return err + } + + if _, ok := msg.(wkt); ok { + out.write("{") + if m.Indent != "" { + out.write("\n") + } + if err := m.marshalTypeURL(out, indent, turl); err != nil { + return err + } + m.writeSep(out) + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + out.write(`"value": `) + } else { + out.write(`"value":`) + } + if err := m.marshalObject(out, msg, indent+m.Indent, ""); err != nil { + return err + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + } + out.write("}") + return out.err + } + + return m.marshalObject(out, msg, indent, turl) +} + +func (m *Marshaler) marshalTypeURL(out *errWriter, indent, typeURL string) error { + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + } + out.write(`"@type":`) + if m.Indent != "" { + out.write(" ") + } + b, err := json.Marshal(typeURL) + if err != nil { + return err + } + out.write(string(b)) + return out.err +} + +// marshalField writes field description and value to the Writer. +func (m *Marshaler) marshalField(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { + if m.Indent != "" { + out.write(indent) + out.write(m.Indent) + } + out.write(`"`) + out.write(prop.JSONName) + out.write(`":`) + if m.Indent != "" { + out.write(" ") + } + if err := m.marshalValue(out, prop, v, indent); err != nil { + return err + } + return nil +} + +// marshalValue writes the value to the Writer. +func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error { + var err error + v = reflect.Indirect(v) + + // Handle nil pointer + if v.Kind() == reflect.Invalid { + out.write("null") + return out.err + } + + // Handle repeated elements. + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 { + out.write("[") + comma := "" + for i := 0; i < v.Len(); i++ { + sliceVal := v.Index(i) + out.write(comma) + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + out.write(m.Indent) + } + if err := m.marshalValue(out, prop, sliceVal, indent+m.Indent); err != nil { + return err + } + comma = "," + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + } + out.write("]") + return out.err + } + + // Handle well-known types. + // Most are handled up in marshalObject (because 99% are messages). + if wkt, ok := v.Interface().(wkt); ok { + switch wkt.XXX_WellKnownType() { + case "NullValue": + out.write("null") + return out.err + } + } + + // Handle enumerations. + if !m.EnumsAsInts && prop.Enum != "" { + // Unknown enum values will are stringified by the proto library as their + // value. Such values should _not_ be quoted or they will be interpreted + // as an enum string instead of their value. + enumStr := v.Interface().(fmt.Stringer).String() + var valStr string + if v.Kind() == reflect.Ptr { + valStr = strconv.Itoa(int(v.Elem().Int())) + } else { + valStr = strconv.Itoa(int(v.Int())) + } + isKnownEnum := enumStr != valStr + if isKnownEnum { + out.write(`"`) + } + out.write(enumStr) + if isKnownEnum { + out.write(`"`) + } + return out.err + } + + // Handle nested messages. + if v.Kind() == reflect.Struct { + return m.marshalObject(out, v.Addr().Interface().(proto.Message), indent+m.Indent, "") + } + + // Handle maps. + // Since Go randomizes map iteration, we sort keys for stable output. + if v.Kind() == reflect.Map { + out.write(`{`) + keys := v.MapKeys() + sort.Sort(mapKeys(keys)) + for i, k := range keys { + if i > 0 { + out.write(`,`) + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + out.write(m.Indent) + } + + b, err := json.Marshal(k.Interface()) + if err != nil { + return err + } + s := string(b) + + // If the JSON is not a string value, encode it again to make it one. + if !strings.HasPrefix(s, `"`) { + b, err := json.Marshal(s) + if err != nil { + return err + } + s = string(b) + } + + out.write(s) + out.write(`:`) + if m.Indent != "" { + out.write(` `) + } + + if err := m.marshalValue(out, prop, v.MapIndex(k), indent+m.Indent); err != nil { + return err + } + } + if m.Indent != "" { + out.write("\n") + out.write(indent) + out.write(m.Indent) + } + out.write(`}`) + return out.err + } + + // Handle non-finite floats, e.g. NaN, Infinity and -Infinity. + if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { + f := v.Float() + var sval string + switch { + case math.IsInf(f, 1): + sval = `"Infinity"` + case math.IsInf(f, -1): + sval = `"-Infinity"` + case math.IsNaN(f): + sval = `"NaN"` + } + if sval != "" { + out.write(sval) + return out.err + } + } + + // Default handling defers to the encoding/json library. + b, err := json.Marshal(v.Interface()) + if err != nil { + return err + } + needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64) + if needToQuote { + out.write(`"`) + } + out.write(string(b)) + if needToQuote { + out.write(`"`) + } + return out.err +} + +// Unmarshaler is a configurable object for converting from a JSON +// representation to a protocol buffer object. +type Unmarshaler struct { + // Whether to allow messages to contain unknown fields, as opposed to + // failing to unmarshal. + AllowUnknownFields bool + + // A custom URL resolver to use when unmarshaling Any messages from JSON. + // If unset, the default resolution strategy is to extract the + // fully-qualified type name from the type URL and pass that to + // proto.MessageType(string). + AnyResolver AnyResolver +} + +// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. +// This function is lenient and will decode any options permutations of the +// related Marshaler. +func (u *Unmarshaler) UnmarshalNext(dec *json.Decoder, pb proto.Message) error { + inputValue := json.RawMessage{} + if err := dec.Decode(&inputValue); err != nil { + return err + } + return u.unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue, nil) +} + +// Unmarshal unmarshals a JSON object stream into a protocol +// buffer. This function is lenient and will decode any options +// permutations of the related Marshaler. +func (u *Unmarshaler) Unmarshal(r io.Reader, pb proto.Message) error { + dec := json.NewDecoder(r) + return u.UnmarshalNext(dec, pb) +} + +// UnmarshalNext unmarshals the next protocol buffer from a JSON object stream. +// This function is lenient and will decode any options permutations of the +// related Marshaler. +func UnmarshalNext(dec *json.Decoder, pb proto.Message) error { + return new(Unmarshaler).UnmarshalNext(dec, pb) +} + +// Unmarshal unmarshals a JSON object stream into a protocol +// buffer. This function is lenient and will decode any options +// permutations of the related Marshaler. +func Unmarshal(r io.Reader, pb proto.Message) error { + return new(Unmarshaler).Unmarshal(r, pb) +} + +// UnmarshalString will populate the fields of a protocol buffer based +// on a JSON string. This function is lenient and will decode any options +// permutations of the related Marshaler. +func UnmarshalString(str string, pb proto.Message) error { + return new(Unmarshaler).Unmarshal(strings.NewReader(str), pb) +} + +// unmarshalValue converts/copies a value into the target. +// prop may be nil. +func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMessage, prop *proto.Properties) error { + targetType := target.Type() + + // Allocate memory for pointer fields. + if targetType.Kind() == reflect.Ptr { + // If input value is "null" and target is a pointer type, then the field should be treated as not set + // UNLESS the target is structpb.Value, in which case it should be set to structpb.NullValue. + _, isJSONPBUnmarshaler := target.Interface().(JSONPBUnmarshaler) + if string(inputValue) == "null" && targetType != reflect.TypeOf(&stpb.Value{}) && !isJSONPBUnmarshaler { + return nil + } + target.Set(reflect.New(targetType.Elem())) + + return u.unmarshalValue(target.Elem(), inputValue, prop) + } + + if jsu, ok := target.Addr().Interface().(JSONPBUnmarshaler); ok { + return jsu.UnmarshalJSONPB(u, []byte(inputValue)) + } + + // Handle well-known types that are not pointers. + if w, ok := target.Addr().Interface().(wkt); ok { + switch w.XXX_WellKnownType() { + case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value", + "Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue": + return u.unmarshalValue(target.Field(0), inputValue, prop) + case "Any": + // Use json.RawMessage pointer type instead of value to support pre-1.8 version. + // 1.8 changed RawMessage.MarshalJSON from pointer type to value type, see + // https://github.com/golang/go/issues/14493 + var jsonFields map[string]*json.RawMessage + if err := json.Unmarshal(inputValue, &jsonFields); err != nil { + return err + } + + val, ok := jsonFields["@type"] + if !ok || val == nil { + return errors.New("Any JSON doesn't have '@type'") + } + + var turl string + if err := json.Unmarshal([]byte(*val), &turl); err != nil { + return fmt.Errorf("can't unmarshal Any's '@type': %q", *val) + } + target.Field(0).SetString(turl) + + var m proto.Message + var err error + if u.AnyResolver != nil { + m, err = u.AnyResolver.Resolve(turl) + } else { + m, err = defaultResolveAny(turl) + } + if err != nil { + return err + } + + if _, ok := m.(wkt); ok { + val, ok := jsonFields["value"] + if !ok { + return errors.New("Any JSON doesn't have 'value'") + } + + if err := u.unmarshalValue(reflect.ValueOf(m).Elem(), *val, nil); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %T: %v", m, err) + } + } else { + delete(jsonFields, "@type") + nestedProto, err := json.Marshal(jsonFields) + if err != nil { + return fmt.Errorf("can't generate JSON for Any's nested proto to be unmarshaled: %v", err) + } + + if err = u.unmarshalValue(reflect.ValueOf(m).Elem(), nestedProto, nil); err != nil { + return fmt.Errorf("can't unmarshal Any nested proto %T: %v", m, err) + } + } + + b, err := proto.Marshal(m) + if err != nil { + return fmt.Errorf("can't marshal proto %T into Any.Value: %v", m, err) + } + target.Field(1).SetBytes(b) + + return nil + case "Duration": + unq, err := strconv.Unquote(string(inputValue)) + if err != nil { + return err + } + + d, err := time.ParseDuration(unq) + if err != nil { + return fmt.Errorf("bad Duration: %v", err) + } + + ns := d.Nanoseconds() + s := ns / 1e9 + ns %= 1e9 + target.Field(0).SetInt(s) + target.Field(1).SetInt(ns) + return nil + case "Timestamp": + unq, err := strconv.Unquote(string(inputValue)) + if err != nil { + return err + } + + t, err := time.Parse(time.RFC3339Nano, unq) + if err != nil { + return fmt.Errorf("bad Timestamp: %v", err) + } + + target.Field(0).SetInt(t.Unix()) + target.Field(1).SetInt(int64(t.Nanosecond())) + return nil + case "Struct": + var m map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &m); err != nil { + return fmt.Errorf("bad StructValue: %v", err) + } + + target.Field(0).Set(reflect.ValueOf(map[string]*stpb.Value{})) + for k, jv := range m { + pv := &stpb.Value{} + if err := u.unmarshalValue(reflect.ValueOf(pv).Elem(), jv, prop); err != nil { + return fmt.Errorf("bad value in StructValue for key %q: %v", k, err) + } + target.Field(0).SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(pv)) + } + return nil + case "ListValue": + var s []json.RawMessage + if err := json.Unmarshal(inputValue, &s); err != nil { + return fmt.Errorf("bad ListValue: %v", err) + } + + target.Field(0).Set(reflect.ValueOf(make([]*stpb.Value, len(s), len(s)))) + for i, sv := range s { + if err := u.unmarshalValue(target.Field(0).Index(i), sv, prop); err != nil { + return err + } + } + return nil + case "Value": + ivStr := string(inputValue) + if ivStr == "null" { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_NullValue{})) + } else if v, err := strconv.ParseFloat(ivStr, 0); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_NumberValue{v})) + } else if v, err := strconv.Unquote(ivStr); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_StringValue{v})) + } else if v, err := strconv.ParseBool(ivStr); err == nil { + target.Field(0).Set(reflect.ValueOf(&stpb.Value_BoolValue{v})) + } else if err := json.Unmarshal(inputValue, &[]json.RawMessage{}); err == nil { + lv := &stpb.ListValue{} + target.Field(0).Set(reflect.ValueOf(&stpb.Value_ListValue{lv})) + return u.unmarshalValue(reflect.ValueOf(lv).Elem(), inputValue, prop) + } else if err := json.Unmarshal(inputValue, &map[string]json.RawMessage{}); err == nil { + sv := &stpb.Struct{} + target.Field(0).Set(reflect.ValueOf(&stpb.Value_StructValue{sv})) + return u.unmarshalValue(reflect.ValueOf(sv).Elem(), inputValue, prop) + } else { + return fmt.Errorf("unrecognized type for Value %q", ivStr) + } + return nil + } + } + + // Handle enums, which have an underlying type of int32, + // and may appear as strings. + // The case of an enum appearing as a number is handled + // at the bottom of this function. + if inputValue[0] == '"' && prop != nil && prop.Enum != "" { + vmap := proto.EnumValueMap(prop.Enum) + // Don't need to do unquoting; valid enum names + // are from a limited character set. + s := inputValue[1 : len(inputValue)-1] + n, ok := vmap[string(s)] + if !ok { + return fmt.Errorf("unknown value %q for enum %s", s, prop.Enum) + } + if target.Kind() == reflect.Ptr { // proto2 + target.Set(reflect.New(targetType.Elem())) + target = target.Elem() + } + target.SetInt(int64(n)) + return nil + } + + // Handle nested messages. + if targetType.Kind() == reflect.Struct { + var jsonFields map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &jsonFields); err != nil { + return err + } + + consumeField := func(prop *proto.Properties) (json.RawMessage, bool) { + // Be liberal in what names we accept; both orig_name and camelName are okay. + fieldNames := acceptedJSONFieldNames(prop) + + vOrig, okOrig := jsonFields[fieldNames.orig] + vCamel, okCamel := jsonFields[fieldNames.camel] + if !okOrig && !okCamel { + return nil, false + } + // If, for some reason, both are present in the data, favour the camelName. + var raw json.RawMessage + if okOrig { + raw = vOrig + delete(jsonFields, fieldNames.orig) + } + if okCamel { + raw = vCamel + delete(jsonFields, fieldNames.camel) + } + return raw, true + } + + sprops := proto.GetProperties(targetType) + for i := 0; i < target.NumField(); i++ { + ft := target.Type().Field(i) + if strings.HasPrefix(ft.Name, "XXX_") { + continue + } + + valueForField, ok := consumeField(sprops.Prop[i]) + if !ok { + continue + } + + if err := u.unmarshalValue(target.Field(i), valueForField, sprops.Prop[i]); err != nil { + return err + } + } + // Check for any oneof fields. + if len(jsonFields) > 0 { + for _, oop := range sprops.OneofTypes { + raw, ok := consumeField(oop.Prop) + if !ok { + continue + } + nv := reflect.New(oop.Type.Elem()) + target.Field(oop.Field).Set(nv) + if err := u.unmarshalValue(nv.Elem().Field(0), raw, oop.Prop); err != nil { + return err + } + } + } + // Handle proto2 extensions. + if len(jsonFields) > 0 { + if ep, ok := target.Addr().Interface().(proto.Message); ok { + for _, ext := range proto.RegisteredExtensions(ep) { + name := fmt.Sprintf("[%s]", ext.Name) + raw, ok := jsonFields[name] + if !ok { + continue + } + delete(jsonFields, name) + nv := reflect.New(reflect.TypeOf(ext.ExtensionType).Elem()) + if err := u.unmarshalValue(nv.Elem(), raw, nil); err != nil { + return err + } + if err := proto.SetExtension(ep, ext, nv.Interface()); err != nil { + return err + } + } + } + } + if !u.AllowUnknownFields && len(jsonFields) > 0 { + // Pick any field to be the scapegoat. + var f string + for fname := range jsonFields { + f = fname + break + } + return fmt.Errorf("unknown field %q in %v", f, targetType) + } + return nil + } + + // Handle arrays (which aren't encoded bytes) + if targetType.Kind() == reflect.Slice && targetType.Elem().Kind() != reflect.Uint8 { + var slc []json.RawMessage + if err := json.Unmarshal(inputValue, &slc); err != nil { + return err + } + if slc != nil { + l := len(slc) + target.Set(reflect.MakeSlice(targetType, l, l)) + for i := 0; i < l; i++ { + if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil { + return err + } + } + } + return nil + } + + // Handle maps (whose keys are always strings) + if targetType.Kind() == reflect.Map { + var mp map[string]json.RawMessage + if err := json.Unmarshal(inputValue, &mp); err != nil { + return err + } + if mp != nil { + target.Set(reflect.MakeMap(targetType)) + var keyprop, valprop *proto.Properties + if prop != nil { + // These could still be nil if the protobuf metadata is broken somehow. + // TODO: This won't work because the fields are unexported. + // We should probably just reparse them. + //keyprop, valprop = prop.mkeyprop, prop.mvalprop + } + for ks, raw := range mp { + // Unmarshal map key. The core json library already decoded the key into a + // string, so we handle that specially. Other types were quoted post-serialization. + var k reflect.Value + if targetType.Key().Kind() == reflect.String { + k = reflect.ValueOf(ks) + } else { + k = reflect.New(targetType.Key()).Elem() + if err := u.unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil { + return err + } + } + + // Unmarshal map value. + v := reflect.New(targetType.Elem()).Elem() + if err := u.unmarshalValue(v, raw, valprop); err != nil { + return err + } + target.SetMapIndex(k, v) + } + } + return nil + } + + // 64-bit integers can be encoded as strings. In this case we drop + // the quotes and proceed as normal. + isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64 + if isNum && strings.HasPrefix(string(inputValue), `"`) { + inputValue = inputValue[1 : len(inputValue)-1] + } + + // Non-finite numbers can be encoded as strings. + isFloat := targetType.Kind() == reflect.Float32 || targetType.Kind() == reflect.Float64 + if isFloat { + if num, ok := nonFinite[string(inputValue)]; ok { + target.SetFloat(num) + return nil + } + } + + // Use the encoding/json for parsing other value types. + return json.Unmarshal(inputValue, target.Addr().Interface()) +} + +// jsonProperties returns parsed proto.Properties for the field and corrects JSONName attribute. +func jsonProperties(f reflect.StructField, origName bool) *proto.Properties { + var prop proto.Properties + prop.Init(f.Type, f.Name, f.Tag.Get("protobuf"), &f) + if origName || prop.JSONName == "" { + prop.JSONName = prop.OrigName + } + return &prop +} + +type fieldNames struct { + orig, camel string +} + +func acceptedJSONFieldNames(prop *proto.Properties) fieldNames { + opts := fieldNames{orig: prop.OrigName, camel: prop.OrigName} + if prop.JSONName != "" { + opts.camel = prop.JSONName + } + return opts +} + +// Writer wrapper inspired by https://blog.golang.org/errors-are-values +type errWriter struct { + writer io.Writer + err error +} + +func (w *errWriter) write(str string) { + if w.err != nil { + return + } + _, w.err = w.writer.Write([]byte(str)) +} + +// Map fields may have key types of non-float scalars, strings and enums. +// The easiest way to sort them in some deterministic order is to use fmt. +// If this turns out to be inefficient we can always consider other options, +// such as doing a Schwartzian transform. +// +// Numeric keys are sorted in numeric order per +// https://developers.google.com/protocol-buffers/docs/proto#maps. +type mapKeys []reflect.Value + +func (s mapKeys) Len() int { return len(s) } +func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s mapKeys) Less(i, j int) bool { + if k := s[i].Kind(); k == s[j].Kind() { + switch k { + case reflect.Int32, reflect.Int64: + return s[i].Int() < s[j].Int() + case reflect.Uint32, reflect.Uint64: + return s[i].Uint() < s[j].Uint() + } + } + return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface()) +} diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go new file mode 100644 index 0000000..4fdbde1 --- /dev/null +++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go @@ -0,0 +1,897 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2015 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package jsonpb + +import ( + "bytes" + "encoding/json" + "io" + "math" + "reflect" + "strings" + "testing" + + "github.com/golang/protobuf/proto" + + pb "github.com/golang/protobuf/jsonpb/jsonpb_test_proto" + proto3pb "github.com/golang/protobuf/proto/proto3_proto" + "github.com/golang/protobuf/ptypes" + anypb "github.com/golang/protobuf/ptypes/any" + durpb "github.com/golang/protobuf/ptypes/duration" + stpb "github.com/golang/protobuf/ptypes/struct" + tspb "github.com/golang/protobuf/ptypes/timestamp" + wpb "github.com/golang/protobuf/ptypes/wrappers" +) + +var ( + marshaler = Marshaler{} + + marshalerAllOptions = Marshaler{ + Indent: " ", + } + + simpleObject = &pb.Simple{ + OInt32: proto.Int32(-32), + OInt64: proto.Int64(-6400000000), + OUint32: proto.Uint32(32), + OUint64: proto.Uint64(6400000000), + OSint32: proto.Int32(-13), + OSint64: proto.Int64(-2600000000), + OFloat: proto.Float32(3.14), + ODouble: proto.Float64(6.02214179e23), + OBool: proto.Bool(true), + OString: proto.String("hello \"there\""), + OBytes: []byte("beep boop"), + } + + simpleObjectJSON = `{` + + `"oBool":true,` + + `"oInt32":-32,` + + `"oInt64":"-6400000000",` + + `"oUint32":32,` + + `"oUint64":"6400000000",` + + `"oSint32":-13,` + + `"oSint64":"-2600000000",` + + `"oFloat":3.14,` + + `"oDouble":6.02214179e+23,` + + `"oString":"hello \"there\"",` + + `"oBytes":"YmVlcCBib29w"` + + `}` + + simpleObjectPrettyJSON = `{ + "oBool": true, + "oInt32": -32, + "oInt64": "-6400000000", + "oUint32": 32, + "oUint64": "6400000000", + "oSint32": -13, + "oSint64": "-2600000000", + "oFloat": 3.14, + "oDouble": 6.02214179e+23, + "oString": "hello \"there\"", + "oBytes": "YmVlcCBib29w" +}` + + repeatsObject = &pb.Repeats{ + RBool: []bool{true, false, true}, + RInt32: []int32{-3, -4, -5}, + RInt64: []int64{-123456789, -987654321}, + RUint32: []uint32{1, 2, 3}, + RUint64: []uint64{6789012345, 3456789012}, + RSint32: []int32{-1, -2, -3}, + RSint64: []int64{-6789012345, -3456789012}, + RFloat: []float32{3.14, 6.28}, + RDouble: []float64{299792458 * 1e20, 6.62606957e-34}, + RString: []string{"happy", "days"}, + RBytes: [][]byte{[]byte("skittles"), []byte("m&m's")}, + } + + repeatsObjectJSON = `{` + + `"rBool":[true,false,true],` + + `"rInt32":[-3,-4,-5],` + + `"rInt64":["-123456789","-987654321"],` + + `"rUint32":[1,2,3],` + + `"rUint64":["6789012345","3456789012"],` + + `"rSint32":[-1,-2,-3],` + + `"rSint64":["-6789012345","-3456789012"],` + + `"rFloat":[3.14,6.28],` + + `"rDouble":[2.99792458e+28,6.62606957e-34],` + + `"rString":["happy","days"],` + + `"rBytes":["c2tpdHRsZXM=","bSZtJ3M="]` + + `}` + + repeatsObjectPrettyJSON = `{ + "rBool": [ + true, + false, + true + ], + "rInt32": [ + -3, + -4, + -5 + ], + "rInt64": [ + "-123456789", + "-987654321" + ], + "rUint32": [ + 1, + 2, + 3 + ], + "rUint64": [ + "6789012345", + "3456789012" + ], + "rSint32": [ + -1, + -2, + -3 + ], + "rSint64": [ + "-6789012345", + "-3456789012" + ], + "rFloat": [ + 3.14, + 6.28 + ], + "rDouble": [ + 2.99792458e+28, + 6.62606957e-34 + ], + "rString": [ + "happy", + "days" + ], + "rBytes": [ + "c2tpdHRsZXM=", + "bSZtJ3M=" + ] +}` + + innerSimple = &pb.Simple{OInt32: proto.Int32(-32)} + innerSimple2 = &pb.Simple{OInt64: proto.Int64(25)} + innerRepeats = &pb.Repeats{RString: []string{"roses", "red"}} + innerRepeats2 = &pb.Repeats{RString: []string{"violets", "blue"}} + complexObject = &pb.Widget{ + Color: pb.Widget_GREEN.Enum(), + RColor: []pb.Widget_Color{pb.Widget_RED, pb.Widget_GREEN, pb.Widget_BLUE}, + Simple: innerSimple, + RSimple: []*pb.Simple{innerSimple, innerSimple2}, + Repeats: innerRepeats, + RRepeats: []*pb.Repeats{innerRepeats, innerRepeats2}, + } + + complexObjectJSON = `{"color":"GREEN",` + + `"rColor":["RED","GREEN","BLUE"],` + + `"simple":{"oInt32":-32},` + + `"rSimple":[{"oInt32":-32},{"oInt64":"25"}],` + + `"repeats":{"rString":["roses","red"]},` + + `"rRepeats":[{"rString":["roses","red"]},{"rString":["violets","blue"]}]` + + `}` + + complexObjectPrettyJSON = `{ + "color": "GREEN", + "rColor": [ + "RED", + "GREEN", + "BLUE" + ], + "simple": { + "oInt32": -32 + }, + "rSimple": [ + { + "oInt32": -32 + }, + { + "oInt64": "25" + } + ], + "repeats": { + "rString": [ + "roses", + "red" + ] + }, + "rRepeats": [ + { + "rString": [ + "roses", + "red" + ] + }, + { + "rString": [ + "violets", + "blue" + ] + } + ] +}` + + colorPrettyJSON = `{ + "color": 2 +}` + + colorListPrettyJSON = `{ + "color": 1000, + "rColor": [ + "RED" + ] +}` + + nummyPrettyJSON = `{ + "nummy": { + "1": 2, + "3": 4 + } +}` + + objjyPrettyJSON = `{ + "objjy": { + "1": { + "dub": 1 + } + } +}` + realNumber = &pb.Real{Value: proto.Float64(3.14159265359)} + realNumberName = "Pi" + complexNumber = &pb.Complex{Imaginary: proto.Float64(0.5772156649)} + realNumberJSON = `{` + + `"value":3.14159265359,` + + `"[jsonpb.Complex.real_extension]":{"imaginary":0.5772156649},` + + `"[jsonpb.name]":"Pi"` + + `}` + + anySimple = &pb.KnownTypes{ + An: &anypb.Any{ + TypeUrl: "something.example.com/jsonpb.Simple", + Value: []byte{ + // &pb.Simple{OBool:true} + 1 << 3, 1, + }, + }, + } + anySimpleJSON = `{"an":{"@type":"something.example.com/jsonpb.Simple","oBool":true}}` + anySimplePrettyJSON = `{ + "an": { + "@type": "something.example.com/jsonpb.Simple", + "oBool": true + } +}` + + anyWellKnown = &pb.KnownTypes{ + An: &anypb.Any{ + TypeUrl: "type.googleapis.com/google.protobuf.Duration", + Value: []byte{ + // &durpb.Duration{Seconds: 1, Nanos: 212000000 } + 1 << 3, 1, // seconds + 2 << 3, 0x80, 0xba, 0x8b, 0x65, // nanos + }, + }, + } + anyWellKnownJSON = `{"an":{"@type":"type.googleapis.com/google.protobuf.Duration","value":"1.212s"}}` + anyWellKnownPrettyJSON = `{ + "an": { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } +}` + + nonFinites = &pb.NonFinites{ + FNan: proto.Float32(float32(math.NaN())), + FPinf: proto.Float32(float32(math.Inf(1))), + FNinf: proto.Float32(float32(math.Inf(-1))), + DNan: proto.Float64(float64(math.NaN())), + DPinf: proto.Float64(float64(math.Inf(1))), + DNinf: proto.Float64(float64(math.Inf(-1))), + } + nonFinitesJSON = `{` + + `"fNan":"NaN",` + + `"fPinf":"Infinity",` + + `"fNinf":"-Infinity",` + + `"dNan":"NaN",` + + `"dPinf":"Infinity",` + + `"dNinf":"-Infinity"` + + `}` +) + +func init() { + if err := proto.SetExtension(realNumber, pb.E_Name, &realNumberName); err != nil { + panic(err) + } + if err := proto.SetExtension(realNumber, pb.E_Complex_RealExtension, complexNumber); err != nil { + panic(err) + } +} + +var marshalingTests = []struct { + desc string + marshaler Marshaler + pb proto.Message + json string +}{ + {"simple flat object", marshaler, simpleObject, simpleObjectJSON}, + {"simple pretty object", marshalerAllOptions, simpleObject, simpleObjectPrettyJSON}, + {"non-finite floats fields object", marshaler, nonFinites, nonFinitesJSON}, + {"repeated fields flat object", marshaler, repeatsObject, repeatsObjectJSON}, + {"repeated fields pretty object", marshalerAllOptions, repeatsObject, repeatsObjectPrettyJSON}, + {"nested message/enum flat object", marshaler, complexObject, complexObjectJSON}, + {"nested message/enum pretty object", marshalerAllOptions, complexObject, complexObjectPrettyJSON}, + {"enum-string flat object", Marshaler{}, + &pb.Widget{Color: pb.Widget_BLUE.Enum()}, `{"color":"BLUE"}`}, + {"enum-value pretty object", Marshaler{EnumsAsInts: true, Indent: " "}, + &pb.Widget{Color: pb.Widget_BLUE.Enum()}, colorPrettyJSON}, + {"unknown enum value object", marshalerAllOptions, + &pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}, colorListPrettyJSON}, + {"repeated proto3 enum", Marshaler{}, + &proto3pb.Message{RFunny: []proto3pb.Message_Humour{ + proto3pb.Message_PUNS, + proto3pb.Message_SLAPSTICK, + }}, + `{"rFunny":["PUNS","SLAPSTICK"]}`}, + {"repeated proto3 enum as int", Marshaler{EnumsAsInts: true}, + &proto3pb.Message{RFunny: []proto3pb.Message_Humour{ + proto3pb.Message_PUNS, + proto3pb.Message_SLAPSTICK, + }}, + `{"rFunny":[1,2]}`}, + {"empty value", marshaler, &pb.Simple3{}, `{}`}, + {"empty value emitted", Marshaler{EmitDefaults: true}, &pb.Simple3{}, `{"dub":0}`}, + {"empty repeated emitted", Marshaler{EmitDefaults: true}, &pb.SimpleSlice3{}, `{"slices":[]}`}, + {"empty map emitted", Marshaler{EmitDefaults: true}, &pb.SimpleMap3{}, `{"stringy":{}}`}, + {"nested struct null", Marshaler{EmitDefaults: true}, &pb.SimpleNull3{}, `{"simple":null}`}, + {"map", marshaler, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, `{"nummy":{"1":2,"3":4}}`}, + {"map", marshalerAllOptions, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, nummyPrettyJSON}, + {"map", marshaler, + &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}, + `{"strry":{"\"one\"":"two","three":"four"}}`}, + {"map", marshaler, + &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}, `{"objjy":{"1":{"dub":1}}}`}, + {"map", marshalerAllOptions, + &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}, objjyPrettyJSON}, + {"map", marshaler, &pb.Mappy{Buggy: map[int64]string{1234: "yup"}}, + `{"buggy":{"1234":"yup"}}`}, + {"map", marshaler, &pb.Mappy{Booly: map[bool]bool{false: true}}, `{"booly":{"false":true}}`}, + // TODO: This is broken. + //{"map", marshaler, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}, `{"enumy":{"XIV":"ROMAN"}`}, + {"map", Marshaler{EnumsAsInts: true}, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}, `{"enumy":{"XIV":2}}`}, + {"map", marshaler, &pb.Mappy{S32Booly: map[int32]bool{1: true, 3: false, 10: true, 12: false}}, `{"s32booly":{"1":true,"3":false,"10":true,"12":false}}`}, + {"map", marshaler, &pb.Mappy{S64Booly: map[int64]bool{1: true, 3: false, 10: true, 12: false}}, `{"s64booly":{"1":true,"3":false,"10":true,"12":false}}`}, + {"map", marshaler, &pb.Mappy{U32Booly: map[uint32]bool{1: true, 3: false, 10: true, 12: false}}, `{"u32booly":{"1":true,"3":false,"10":true,"12":false}}`}, + {"map", marshaler, &pb.Mappy{U64Booly: map[uint64]bool{1: true, 3: false, 10: true, 12: false}}, `{"u64booly":{"1":true,"3":false,"10":true,"12":false}}`}, + {"proto2 map", marshaler, &pb.Maps{MInt64Str: map[int64]string{213: "cat"}}, + `{"mInt64Str":{"213":"cat"}}`}, + {"proto2 map", marshaler, + &pb.Maps{MBoolSimple: map[bool]*pb.Simple{true: {OInt32: proto.Int32(1)}}}, + `{"mBoolSimple":{"true":{"oInt32":1}}}`}, + {"oneof, not set", marshaler, &pb.MsgWithOneof{}, `{}`}, + {"oneof, set", marshaler, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Title{"Grand Poobah"}}, `{"title":"Grand Poobah"}`}, + {"force orig_name", Marshaler{OrigName: true}, &pb.Simple{OInt32: proto.Int32(4)}, + `{"o_int32":4}`}, + {"proto2 extension", marshaler, realNumber, realNumberJSON}, + {"Any with message", marshaler, anySimple, anySimpleJSON}, + {"Any with message and indent", marshalerAllOptions, anySimple, anySimplePrettyJSON}, + {"Any with WKT", marshaler, anyWellKnown, anyWellKnownJSON}, + {"Any with WKT and indent", marshalerAllOptions, anyWellKnown, anyWellKnownPrettyJSON}, + {"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}, `{"dur":"3.000s"}`}, + {"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 100000000, Nanos: 1}}, `{"dur":"100000000.000000001s"}`}, + {"Struct", marshaler, &pb.KnownTypes{St: &stpb.Struct{ + Fields: map[string]*stpb.Value{ + "one": {Kind: &stpb.Value_StringValue{"loneliest number"}}, + "two": {Kind: &stpb.Value_NullValue{stpb.NullValue_NULL_VALUE}}, + }, + }}, `{"st":{"one":"loneliest number","two":null}}`}, + {"empty ListValue", marshaler, &pb.KnownTypes{Lv: &stpb.ListValue{}}, `{"lv":[]}`}, + {"basic ListValue", marshaler, &pb.KnownTypes{Lv: &stpb.ListValue{Values: []*stpb.Value{ + {Kind: &stpb.Value_StringValue{"x"}}, + {Kind: &stpb.Value_NullValue{}}, + {Kind: &stpb.Value_NumberValue{3}}, + {Kind: &stpb.Value_BoolValue{true}}, + }}}, `{"lv":["x",null,3,true]}`}, + {"Timestamp", marshaler, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 14e8, Nanos: 21e6}}, `{"ts":"2014-05-13T16:53:20.021Z"}`}, + {"number Value", marshaler, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NumberValue{1}}}, `{"val":1}`}, + {"null Value", marshaler, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NullValue{stpb.NullValue_NULL_VALUE}}}, `{"val":null}`}, + {"string number value", marshaler, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_StringValue{"9223372036854775807"}}}, `{"val":"9223372036854775807"}`}, + {"list of lists Value", marshaler, &pb.KnownTypes{Val: &stpb.Value{ + Kind: &stpb.Value_ListValue{&stpb.ListValue{ + Values: []*stpb.Value{ + {Kind: &stpb.Value_StringValue{"x"}}, + {Kind: &stpb.Value_ListValue{&stpb.ListValue{ + Values: []*stpb.Value{ + {Kind: &stpb.Value_ListValue{&stpb.ListValue{ + Values: []*stpb.Value{{Kind: &stpb.Value_StringValue{"y"}}}, + }}}, + {Kind: &stpb.Value_StringValue{"z"}}, + }, + }}}, + }, + }}, + }}, `{"val":["x",[["y"],"z"]]}`}, + + {"DoubleValue", marshaler, &pb.KnownTypes{Dbl: &wpb.DoubleValue{Value: 1.2}}, `{"dbl":1.2}`}, + {"FloatValue", marshaler, &pb.KnownTypes{Flt: &wpb.FloatValue{Value: 1.2}}, `{"flt":1.2}`}, + {"Int64Value", marshaler, &pb.KnownTypes{I64: &wpb.Int64Value{Value: -3}}, `{"i64":"-3"}`}, + {"UInt64Value", marshaler, &pb.KnownTypes{U64: &wpb.UInt64Value{Value: 3}}, `{"u64":"3"}`}, + {"Int32Value", marshaler, &pb.KnownTypes{I32: &wpb.Int32Value{Value: -4}}, `{"i32":-4}`}, + {"UInt32Value", marshaler, &pb.KnownTypes{U32: &wpb.UInt32Value{Value: 4}}, `{"u32":4}`}, + {"BoolValue", marshaler, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}, `{"bool":true}`}, + {"StringValue", marshaler, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}, `{"str":"plush"}`}, + {"BytesValue", marshaler, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}, `{"bytes":"d293"}`}, +} + +func TestMarshaling(t *testing.T) { + for _, tt := range marshalingTests { + json, err := tt.marshaler.MarshalToString(tt.pb) + if err != nil { + t.Errorf("%s: marshaling error: %v", tt.desc, err) + } else if tt.json != json { + t.Errorf("%s: got [%v] want [%v]", tt.desc, json, tt.json) + } + } +} + +func TestMarshalJSONPBMarshaler(t *testing.T) { + rawJson := `{ "foo": "bar", "baz": [0, 1, 2, 3] }` + msg := dynamicMessage{rawJson: rawJson} + str, err := new(Marshaler).MarshalToString(&msg) + if err != nil { + t.Errorf("an unexpected error occurred when marshalling JSONPBMarshaler: %v", err) + } + if str != rawJson { + t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, rawJson) + } +} + +func TestMarshalAnyJSONPBMarshaler(t *testing.T) { + msg := dynamicMessage{rawJson: `{ "foo": "bar", "baz": [0, 1, 2, 3] }`} + a, err := ptypes.MarshalAny(&msg) + if err != nil { + t.Errorf("an unexpected error occurred when marshalling to Any: %v", err) + } + str, err := new(Marshaler).MarshalToString(a) + if err != nil { + t.Errorf("an unexpected error occurred when marshalling Any to JSON: %v", err) + } + // after custom marshaling, it's round-tripped through JSON decoding/encoding already, + // so the keys are sorted, whitespace is compacted, and "@type" key has been added + expected := `{"@type":"type.googleapis.com/` + dynamicMessageName + `","baz":[0,1,2,3],"foo":"bar"}` + if str != expected { + t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, expected) + } +} + +var unmarshalingTests = []struct { + desc string + unmarshaler Unmarshaler + json string + pb proto.Message +}{ + {"simple flat object", Unmarshaler{}, simpleObjectJSON, simpleObject}, + {"simple pretty object", Unmarshaler{}, simpleObjectPrettyJSON, simpleObject}, + {"repeated fields flat object", Unmarshaler{}, repeatsObjectJSON, repeatsObject}, + {"repeated fields pretty object", Unmarshaler{}, repeatsObjectPrettyJSON, repeatsObject}, + {"nested message/enum flat object", Unmarshaler{}, complexObjectJSON, complexObject}, + {"nested message/enum pretty object", Unmarshaler{}, complexObjectPrettyJSON, complexObject}, + {"enum-string object", Unmarshaler{}, `{"color":"BLUE"}`, &pb.Widget{Color: pb.Widget_BLUE.Enum()}}, + {"enum-value object", Unmarshaler{}, "{\n \"color\": 2\n}", &pb.Widget{Color: pb.Widget_BLUE.Enum()}}, + {"unknown field with allowed option", Unmarshaler{AllowUnknownFields: true}, `{"unknown": "foo"}`, new(pb.Simple)}, + {"proto3 enum string", Unmarshaler{}, `{"hilarity":"PUNS"}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}}, + {"proto3 enum value", Unmarshaler{}, `{"hilarity":1}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}}, + {"unknown enum value object", + Unmarshaler{}, + "{\n \"color\": 1000,\n \"r_color\": [\n \"RED\"\n ]\n}", + &pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}}, + {"repeated proto3 enum", Unmarshaler{}, `{"rFunny":["PUNS","SLAPSTICK"]}`, + &proto3pb.Message{RFunny: []proto3pb.Message_Humour{ + proto3pb.Message_PUNS, + proto3pb.Message_SLAPSTICK, + }}}, + {"repeated proto3 enum as int", Unmarshaler{}, `{"rFunny":[1,2]}`, + &proto3pb.Message{RFunny: []proto3pb.Message_Humour{ + proto3pb.Message_PUNS, + proto3pb.Message_SLAPSTICK, + }}}, + {"repeated proto3 enum as mix of strings and ints", Unmarshaler{}, `{"rFunny":["PUNS",2]}`, + &proto3pb.Message{RFunny: []proto3pb.Message_Humour{ + proto3pb.Message_PUNS, + proto3pb.Message_SLAPSTICK, + }}}, + {"unquoted int64 object", Unmarshaler{}, `{"oInt64":-314}`, &pb.Simple{OInt64: proto.Int64(-314)}}, + {"unquoted uint64 object", Unmarshaler{}, `{"oUint64":123}`, &pb.Simple{OUint64: proto.Uint64(123)}}, + {"NaN", Unmarshaler{}, `{"oDouble":"NaN"}`, &pb.Simple{ODouble: proto.Float64(math.NaN())}}, + {"Inf", Unmarshaler{}, `{"oFloat":"Infinity"}`, &pb.Simple{OFloat: proto.Float32(float32(math.Inf(1)))}}, + {"-Inf", Unmarshaler{}, `{"oDouble":"-Infinity"}`, &pb.Simple{ODouble: proto.Float64(math.Inf(-1))}}, + {"map", Unmarshaler{}, `{"nummy":{"1":2,"3":4}}`, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}}, + {"map", Unmarshaler{}, `{"strry":{"\"one\"":"two","three":"four"}}`, &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}}, + {"map", Unmarshaler{}, `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}}, + {"proto2 extension", Unmarshaler{}, realNumberJSON, realNumber}, + {"Any with message", Unmarshaler{}, anySimpleJSON, anySimple}, + {"Any with message and indent", Unmarshaler{}, anySimplePrettyJSON, anySimple}, + {"Any with WKT", Unmarshaler{}, anyWellKnownJSON, anyWellKnown}, + {"Any with WKT and indent", Unmarshaler{}, anyWellKnownPrettyJSON, anyWellKnown}, + // TODO: This is broken. + //{"map", Unmarshaler{}, `{"enumy":{"XIV":"ROMAN"}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}}, + {"map", Unmarshaler{}, `{"enumy":{"XIV":2}}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}}, + {"oneof", Unmarshaler{}, `{"salary":31000}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Salary{31000}}}, + {"oneof spec name", Unmarshaler{}, `{"Country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}}, + {"oneof orig_name", Unmarshaler{}, `{"Country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}}, + {"oneof spec name2", Unmarshaler{}, `{"homeAddress":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_HomeAddress{"Australia"}}}, + {"oneof orig_name2", Unmarshaler{}, `{"home_address":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_HomeAddress{"Australia"}}}, + {"orig_name input", Unmarshaler{}, `{"o_bool":true}`, &pb.Simple{OBool: proto.Bool(true)}}, + {"camelName input", Unmarshaler{}, `{"oBool":true}`, &pb.Simple{OBool: proto.Bool(true)}}, + + {"Duration", Unmarshaler{}, `{"dur":"3.000s"}`, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}}, + {"null Duration", Unmarshaler{}, `{"dur":null}`, &pb.KnownTypes{Dur: nil}}, + {"Timestamp", Unmarshaler{}, `{"ts":"2014-05-13T16:53:20.021Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 14e8, Nanos: 21e6}}}, + {"PreEpochTimestamp", Unmarshaler{}, `{"ts":"1969-12-31T23:59:58.999999995Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: -2, Nanos: 999999995}}}, + {"ZeroTimeTimestamp", Unmarshaler{}, `{"ts":"0001-01-01T00:00:00Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: -62135596800, Nanos: 0}}}, + {"null Timestamp", Unmarshaler{}, `{"ts":null}`, &pb.KnownTypes{Ts: nil}}, + {"null Struct", Unmarshaler{}, `{"st": null}`, &pb.KnownTypes{St: nil}}, + {"empty Struct", Unmarshaler{}, `{"st": {}}`, &pb.KnownTypes{St: &stpb.Struct{}}}, + {"basic Struct", Unmarshaler{}, `{"st": {"a": "x", "b": null, "c": 3, "d": true}}`, &pb.KnownTypes{St: &stpb.Struct{Fields: map[string]*stpb.Value{ + "a": {Kind: &stpb.Value_StringValue{"x"}}, + "b": {Kind: &stpb.Value_NullValue{}}, + "c": {Kind: &stpb.Value_NumberValue{3}}, + "d": {Kind: &stpb.Value_BoolValue{true}}, + }}}}, + {"nested Struct", Unmarshaler{}, `{"st": {"a": {"b": 1, "c": [{"d": true}, "f"]}}}`, &pb.KnownTypes{St: &stpb.Struct{Fields: map[string]*stpb.Value{ + "a": {Kind: &stpb.Value_StructValue{&stpb.Struct{Fields: map[string]*stpb.Value{ + "b": {Kind: &stpb.Value_NumberValue{1}}, + "c": {Kind: &stpb.Value_ListValue{&stpb.ListValue{Values: []*stpb.Value{ + {Kind: &stpb.Value_StructValue{&stpb.Struct{Fields: map[string]*stpb.Value{"d": {Kind: &stpb.Value_BoolValue{true}}}}}}, + {Kind: &stpb.Value_StringValue{"f"}}, + }}}}, + }}}}, + }}}}, + {"null ListValue", Unmarshaler{}, `{"lv": null}`, &pb.KnownTypes{Lv: nil}}, + {"empty ListValue", Unmarshaler{}, `{"lv": []}`, &pb.KnownTypes{Lv: &stpb.ListValue{}}}, + {"basic ListValue", Unmarshaler{}, `{"lv": ["x", null, 3, true]}`, &pb.KnownTypes{Lv: &stpb.ListValue{Values: []*stpb.Value{ + {Kind: &stpb.Value_StringValue{"x"}}, + {Kind: &stpb.Value_NullValue{}}, + {Kind: &stpb.Value_NumberValue{3}}, + {Kind: &stpb.Value_BoolValue{true}}, + }}}}, + {"number Value", Unmarshaler{}, `{"val":1}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NumberValue{1}}}}, + {"null Value", Unmarshaler{}, `{"val":null}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NullValue{stpb.NullValue_NULL_VALUE}}}}, + {"bool Value", Unmarshaler{}, `{"val":true}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_BoolValue{true}}}}, + {"string Value", Unmarshaler{}, `{"val":"x"}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_StringValue{"x"}}}}, + {"string number value", Unmarshaler{}, `{"val":"9223372036854775807"}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_StringValue{"9223372036854775807"}}}}, + {"list of lists Value", Unmarshaler{}, `{"val":["x", [["y"], "z"]]}`, &pb.KnownTypes{Val: &stpb.Value{ + Kind: &stpb.Value_ListValue{&stpb.ListValue{ + Values: []*stpb.Value{ + {Kind: &stpb.Value_StringValue{"x"}}, + {Kind: &stpb.Value_ListValue{&stpb.ListValue{ + Values: []*stpb.Value{ + {Kind: &stpb.Value_ListValue{&stpb.ListValue{ + Values: []*stpb.Value{{Kind: &stpb.Value_StringValue{"y"}}}, + }}}, + {Kind: &stpb.Value_StringValue{"z"}}, + }, + }}}, + }, + }}}}}, + + {"DoubleValue", Unmarshaler{}, `{"dbl":1.2}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{Value: 1.2}}}, + {"FloatValue", Unmarshaler{}, `{"flt":1.2}`, &pb.KnownTypes{Flt: &wpb.FloatValue{Value: 1.2}}}, + {"Int64Value", Unmarshaler{}, `{"i64":"-3"}`, &pb.KnownTypes{I64: &wpb.Int64Value{Value: -3}}}, + {"UInt64Value", Unmarshaler{}, `{"u64":"3"}`, &pb.KnownTypes{U64: &wpb.UInt64Value{Value: 3}}}, + {"Int32Value", Unmarshaler{}, `{"i32":-4}`, &pb.KnownTypes{I32: &wpb.Int32Value{Value: -4}}}, + {"UInt32Value", Unmarshaler{}, `{"u32":4}`, &pb.KnownTypes{U32: &wpb.UInt32Value{Value: 4}}}, + {"BoolValue", Unmarshaler{}, `{"bool":true}`, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}}, + {"StringValue", Unmarshaler{}, `{"str":"plush"}`, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}}, + {"BytesValue", Unmarshaler{}, `{"bytes":"d293"}`, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}}, + + // Ensure that `null` as a value ends up with a nil pointer instead of a [type]Value struct. + {"null DoubleValue", Unmarshaler{}, `{"dbl":null}`, &pb.KnownTypes{Dbl: nil}}, + {"null FloatValue", Unmarshaler{}, `{"flt":null}`, &pb.KnownTypes{Flt: nil}}, + {"null Int64Value", Unmarshaler{}, `{"i64":null}`, &pb.KnownTypes{I64: nil}}, + {"null UInt64Value", Unmarshaler{}, `{"u64":null}`, &pb.KnownTypes{U64: nil}}, + {"null Int32Value", Unmarshaler{}, `{"i32":null}`, &pb.KnownTypes{I32: nil}}, + {"null UInt32Value", Unmarshaler{}, `{"u32":null}`, &pb.KnownTypes{U32: nil}}, + {"null BoolValue", Unmarshaler{}, `{"bool":null}`, &pb.KnownTypes{Bool: nil}}, + {"null StringValue", Unmarshaler{}, `{"str":null}`, &pb.KnownTypes{Str: nil}}, + {"null BytesValue", Unmarshaler{}, `{"bytes":null}`, &pb.KnownTypes{Bytes: nil}}, +} + +func TestUnmarshaling(t *testing.T) { + for _, tt := range unmarshalingTests { + // Make a new instance of the type of our expected object. + p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message) + + err := tt.unmarshaler.Unmarshal(strings.NewReader(tt.json), p) + if err != nil { + t.Errorf("%s: %v", tt.desc, err) + continue + } + + // For easier diffs, compare text strings of the protos. + exp := proto.MarshalTextString(tt.pb) + act := proto.MarshalTextString(p) + if string(exp) != string(act) { + t.Errorf("%s: got [%s] want [%s]", tt.desc, act, exp) + } + } +} + +func TestUnmarshalNullArray(t *testing.T) { + var repeats pb.Repeats + if err := UnmarshalString(`{"rBool":null}`, &repeats); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(repeats, pb.Repeats{}) { + t.Errorf("got non-nil fields in [%#v]", repeats) + } +} + +func TestUnmarshalNullObject(t *testing.T) { + var maps pb.Maps + if err := UnmarshalString(`{"mInt64Str":null}`, &maps); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(maps, pb.Maps{}) { + t.Errorf("got non-nil fields in [%#v]", maps) + } +} + +func TestUnmarshalNext(t *testing.T) { + // We only need to check against a few, not all of them. + tests := unmarshalingTests[:5] + + // Create a buffer with many concatenated JSON objects. + var b bytes.Buffer + for _, tt := range tests { + b.WriteString(tt.json) + } + + dec := json.NewDecoder(&b) + for _, tt := range tests { + // Make a new instance of the type of our expected object. + p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message) + + err := tt.unmarshaler.UnmarshalNext(dec, p) + if err != nil { + t.Errorf("%s: %v", tt.desc, err) + continue + } + + // For easier diffs, compare text strings of the protos. + exp := proto.MarshalTextString(tt.pb) + act := proto.MarshalTextString(p) + if string(exp) != string(act) { + t.Errorf("%s: got [%s] want [%s]", tt.desc, act, exp) + } + } + + p := &pb.Simple{} + err := new(Unmarshaler).UnmarshalNext(dec, p) + if err != io.EOF { + t.Errorf("eof: got %v, expected io.EOF", err) + } +} + +var unmarshalingShouldError = []struct { + desc string + in string + pb proto.Message +}{ + {"a value", "666", new(pb.Simple)}, + {"gibberish", "{adskja123;l23=-=", new(pb.Simple)}, + {"unknown field", `{"unknown": "foo"}`, new(pb.Simple)}, + {"unknown enum name", `{"hilarity":"DAVE"}`, new(proto3pb.Message)}, +} + +func TestUnmarshalingBadInput(t *testing.T) { + for _, tt := range unmarshalingShouldError { + err := UnmarshalString(tt.in, tt.pb) + if err == nil { + t.Errorf("an error was expected when parsing %q instead of an object", tt.desc) + } + } +} + +type funcResolver func(turl string) (proto.Message, error) + +func (fn funcResolver) Resolve(turl string) (proto.Message, error) { + return fn(turl) +} + +func TestAnyWithCustomResolver(t *testing.T) { + var resolvedTypeUrls []string + resolver := funcResolver(func(turl string) (proto.Message, error) { + resolvedTypeUrls = append(resolvedTypeUrls, turl) + return new(pb.Simple), nil + }) + msg := &pb.Simple{ + OBytes: []byte{1, 2, 3, 4}, + OBool: proto.Bool(true), + OString: proto.String("foobar"), + OInt64: proto.Int64(1020304), + } + msgBytes, err := proto.Marshal(msg) + if err != nil { + t.Errorf("an unexpected error occurred when marshaling message: %v", err) + } + // make an Any with a type URL that won't resolve w/out custom resolver + any := &anypb.Any{ + TypeUrl: "https://foobar.com/some.random.MessageKind", + Value: msgBytes, + } + + m := Marshaler{AnyResolver: resolver} + js, err := m.MarshalToString(any) + if err != nil { + t.Errorf("an unexpected error occurred when marshaling any to JSON: %v", err) + } + if len(resolvedTypeUrls) != 1 { + t.Errorf("custom resolver was not invoked during marshaling") + } else if resolvedTypeUrls[0] != "https://foobar.com/some.random.MessageKind" { + t.Errorf("custom resolver was invoked with wrong URL: got %q, wanted %q", resolvedTypeUrls[0], "https://foobar.com/some.random.MessageKind") + } + wanted := `{"@type":"https://foobar.com/some.random.MessageKind","oBool":true,"oInt64":"1020304","oString":"foobar","oBytes":"AQIDBA=="}` + if js != wanted { + t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", js, wanted) + } + + u := Unmarshaler{AnyResolver: resolver} + roundTrip := &anypb.Any{} + err = u.Unmarshal(bytes.NewReader([]byte(js)), roundTrip) + if err != nil { + t.Errorf("an unexpected error occurred when unmarshaling any from JSON: %v", err) + } + if len(resolvedTypeUrls) != 2 { + t.Errorf("custom resolver was not invoked during marshaling") + } else if resolvedTypeUrls[1] != "https://foobar.com/some.random.MessageKind" { + t.Errorf("custom resolver was invoked with wrong URL: got %q, wanted %q", resolvedTypeUrls[1], "https://foobar.com/some.random.MessageKind") + } + if !proto.Equal(any, roundTrip) { + t.Errorf("message contents not set correctly after unmarshalling JSON: got %s, wanted %s", roundTrip, any) + } +} + +func TestUnmarshalJSONPBUnmarshaler(t *testing.T) { + rawJson := `{ "foo": "bar", "baz": [0, 1, 2, 3] }` + var msg dynamicMessage + if err := Unmarshal(strings.NewReader(rawJson), &msg); err != nil { + t.Errorf("an unexpected error occurred when parsing into JSONPBUnmarshaler: %v", err) + } + if msg.rawJson != rawJson { + t.Errorf("message contents not set correctly after unmarshalling JSON: got %s, wanted %s", msg.rawJson, rawJson) + } +} + +func TestUnmarshalNullWithJSONPBUnmarshaler(t *testing.T) { + rawJson := `{"stringField":null}` + var ptrFieldMsg ptrFieldMessage + if err := Unmarshal(strings.NewReader(rawJson), &ptrFieldMsg); err != nil { + t.Errorf("unmarshal error: %v", err) + } + + want := ptrFieldMessage{StringField: &stringField{IsSet: true, StringValue: "null"}} + if !proto.Equal(&ptrFieldMsg, &want) { + t.Errorf("unmarshal result StringField: got %v, want %v", ptrFieldMsg, want) + } +} + +func TestUnmarshalAnyJSONPBUnmarshaler(t *testing.T) { + rawJson := `{ "@type": "blah.com/` + dynamicMessageName + `", "foo": "bar", "baz": [0, 1, 2, 3] }` + var got anypb.Any + if err := Unmarshal(strings.NewReader(rawJson), &got); err != nil { + t.Errorf("an unexpected error occurred when parsing into JSONPBUnmarshaler: %v", err) + } + + dm := &dynamicMessage{rawJson: `{"baz":[0,1,2,3],"foo":"bar"}`} + var want anypb.Any + if b, err := proto.Marshal(dm); err != nil { + t.Errorf("an unexpected error occurred when marshaling message: %v", err) + } else { + want.TypeUrl = "blah.com/" + dynamicMessageName + want.Value = b + } + + if !proto.Equal(&got, &want) { + t.Errorf("message contents not set correctly after unmarshalling JSON: got %s, wanted %s", got, want) + } +} + +const ( + dynamicMessageName = "google.protobuf.jsonpb.testing.dynamicMessage" +) + +func init() { + // we register the custom type below so that we can use it in Any types + proto.RegisterType((*dynamicMessage)(nil), dynamicMessageName) +} + +type ptrFieldMessage struct { + StringField *stringField `protobuf:"bytes,1,opt,name=stringField"` +} + +func (m *ptrFieldMessage) Reset() { +} + +func (m *ptrFieldMessage) String() string { + return m.StringField.StringValue +} + +func (m *ptrFieldMessage) ProtoMessage() { +} + +type stringField struct { + IsSet bool `protobuf:"varint,1,opt,name=isSet"` + StringValue string `protobuf:"bytes,2,opt,name=stringValue"` +} + +func (s *stringField) Reset() { +} + +func (s *stringField) String() string { + return s.StringValue +} + +func (s *stringField) ProtoMessage() { +} + +func (s *stringField) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error { + s.IsSet = true + s.StringValue = string(js) + return nil +} + +// dynamicMessage implements protobuf.Message but is not a normal generated message type. +// It provides implementations of JSONPBMarshaler and JSONPBUnmarshaler for JSON support. +type dynamicMessage struct { + rawJson string `protobuf:"bytes,1,opt,name=rawJson"` +} + +func (m *dynamicMessage) Reset() { + m.rawJson = "{}" +} + +func (m *dynamicMessage) String() string { + return m.rawJson +} + +func (m *dynamicMessage) ProtoMessage() { +} + +func (m *dynamicMessage) MarshalJSONPB(jm *Marshaler) ([]byte, error) { + return []byte(m.rawJson), nil +} + +func (m *dynamicMessage) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error { + m.rawJson = string(js) + return nil +} diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go new file mode 100644 index 0000000..4cfe608 --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go @@ -0,0 +1,380 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: google/protobuf/struct.proto + +/* +Package structpb is a generated protocol buffer package. + +It is generated from these files: + google/protobuf/struct.proto + +It has these top-level messages: + Struct + Value + ListValue +*/ +package structpb + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +type NullValue int32 + +const ( + // Null value. + NullValue_NULL_VALUE NullValue = 0 +) + +var NullValue_name = map[int32]string{ + 0: "NULL_VALUE", +} +var NullValue_value = map[string]int32{ + "NULL_VALUE": 0, +} + +func (x NullValue) String() string { + return proto.EnumName(NullValue_name, int32(x)) +} +func (NullValue) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (NullValue) XXX_WellKnownType() string { return "NullValue" } + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +type Struct struct { + // Unordered map of dynamically typed values. + Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` +} + +func (m *Struct) Reset() { *m = Struct{} } +func (m *Struct) String() string { return proto.CompactTextString(m) } +func (*Struct) ProtoMessage() {} +func (*Struct) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +func (*Struct) XXX_WellKnownType() string { return "Struct" } + +func (m *Struct) GetFields() map[string]*Value { + if m != nil { + return m.Fields + } + return nil +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of that +// variants, absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +type Value struct { + // The kind of value. + // + // Types that are valid to be assigned to Kind: + // *Value_NullValue + // *Value_NumberValue + // *Value_StringValue + // *Value_BoolValue + // *Value_StructValue + // *Value_ListValue + Kind isValue_Kind `protobuf_oneof:"kind"` +} + +func (m *Value) Reset() { *m = Value{} } +func (m *Value) String() string { return proto.CompactTextString(m) } +func (*Value) ProtoMessage() {} +func (*Value) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (*Value) XXX_WellKnownType() string { return "Value" } + +type isValue_Kind interface { + isValue_Kind() +} + +type Value_NullValue struct { + NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,enum=google.protobuf.NullValue,oneof"` +} +type Value_NumberValue struct { + NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,oneof"` +} +type Value_StringValue struct { + StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,oneof"` +} +type Value_BoolValue struct { + BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,oneof"` +} +type Value_StructValue struct { + StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,oneof"` +} +type Value_ListValue struct { + ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,oneof"` +} + +func (*Value_NullValue) isValue_Kind() {} +func (*Value_NumberValue) isValue_Kind() {} +func (*Value_StringValue) isValue_Kind() {} +func (*Value_BoolValue) isValue_Kind() {} +func (*Value_StructValue) isValue_Kind() {} +func (*Value_ListValue) isValue_Kind() {} + +func (m *Value) GetKind() isValue_Kind { + if m != nil { + return m.Kind + } + return nil +} + +func (m *Value) GetNullValue() NullValue { + if x, ok := m.GetKind().(*Value_NullValue); ok { + return x.NullValue + } + return NullValue_NULL_VALUE +} + +func (m *Value) GetNumberValue() float64 { + if x, ok := m.GetKind().(*Value_NumberValue); ok { + return x.NumberValue + } + return 0 +} + +func (m *Value) GetStringValue() string { + if x, ok := m.GetKind().(*Value_StringValue); ok { + return x.StringValue + } + return "" +} + +func (m *Value) GetBoolValue() bool { + if x, ok := m.GetKind().(*Value_BoolValue); ok { + return x.BoolValue + } + return false +} + +func (m *Value) GetStructValue() *Struct { + if x, ok := m.GetKind().(*Value_StructValue); ok { + return x.StructValue + } + return nil +} + +func (m *Value) GetListValue() *ListValue { + if x, ok := m.GetKind().(*Value_ListValue); ok { + return x.ListValue + } + return nil +} + +// XXX_OneofFuncs is for the internal use of the proto package. +func (*Value) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { + return _Value_OneofMarshaler, _Value_OneofUnmarshaler, _Value_OneofSizer, []interface{}{ + (*Value_NullValue)(nil), + (*Value_NumberValue)(nil), + (*Value_StringValue)(nil), + (*Value_BoolValue)(nil), + (*Value_StructValue)(nil), + (*Value_ListValue)(nil), + } +} + +func _Value_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { + m := msg.(*Value) + // kind + switch x := m.Kind.(type) { + case *Value_NullValue: + b.EncodeVarint(1<<3 | proto.WireVarint) + b.EncodeVarint(uint64(x.NullValue)) + case *Value_NumberValue: + b.EncodeVarint(2<<3 | proto.WireFixed64) + b.EncodeFixed64(math.Float64bits(x.NumberValue)) + case *Value_StringValue: + b.EncodeVarint(3<<3 | proto.WireBytes) + b.EncodeStringBytes(x.StringValue) + case *Value_BoolValue: + t := uint64(0) + if x.BoolValue { + t = 1 + } + b.EncodeVarint(4<<3 | proto.WireVarint) + b.EncodeVarint(t) + case *Value_StructValue: + b.EncodeVarint(5<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.StructValue); err != nil { + return err + } + case *Value_ListValue: + b.EncodeVarint(6<<3 | proto.WireBytes) + if err := b.EncodeMessage(x.ListValue); err != nil { + return err + } + case nil: + default: + return fmt.Errorf("Value.Kind has unexpected type %T", x) + } + return nil +} + +func _Value_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { + m := msg.(*Value) + switch tag { + case 1: // kind.null_value + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Kind = &Value_NullValue{NullValue(x)} + return true, err + case 2: // kind.number_value + if wire != proto.WireFixed64 { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeFixed64() + m.Kind = &Value_NumberValue{math.Float64frombits(x)} + return true, err + case 3: // kind.string_value + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeStringBytes() + m.Kind = &Value_StringValue{x} + return true, err + case 4: // kind.bool_value + if wire != proto.WireVarint { + return true, proto.ErrInternalBadWireType + } + x, err := b.DecodeVarint() + m.Kind = &Value_BoolValue{x != 0} + return true, err + case 5: // kind.struct_value + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(Struct) + err := b.DecodeMessage(msg) + m.Kind = &Value_StructValue{msg} + return true, err + case 6: // kind.list_value + if wire != proto.WireBytes { + return true, proto.ErrInternalBadWireType + } + msg := new(ListValue) + err := b.DecodeMessage(msg) + m.Kind = &Value_ListValue{msg} + return true, err + default: + return false, nil + } +} + +func _Value_OneofSizer(msg proto.Message) (n int) { + m := msg.(*Value) + // kind + switch x := m.Kind.(type) { + case *Value_NullValue: + n += proto.SizeVarint(1<<3 | proto.WireVarint) + n += proto.SizeVarint(uint64(x.NullValue)) + case *Value_NumberValue: + n += proto.SizeVarint(2<<3 | proto.WireFixed64) + n += 8 + case *Value_StringValue: + n += proto.SizeVarint(3<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(len(x.StringValue))) + n += len(x.StringValue) + case *Value_BoolValue: + n += proto.SizeVarint(4<<3 | proto.WireVarint) + n += 1 + case *Value_StructValue: + s := proto.Size(x.StructValue) + n += proto.SizeVarint(5<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case *Value_ListValue: + s := proto.Size(x.ListValue) + n += proto.SizeVarint(6<<3 | proto.WireBytes) + n += proto.SizeVarint(uint64(s)) + n += s + case nil: + default: + panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) + } + return n +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +type ListValue struct { + // Repeated field of dynamically typed values. + Values []*Value `protobuf:"bytes,1,rep,name=values" json:"values,omitempty"` +} + +func (m *ListValue) Reset() { *m = ListValue{} } +func (m *ListValue) String() string { return proto.CompactTextString(m) } +func (*ListValue) ProtoMessage() {} +func (*ListValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*ListValue) XXX_WellKnownType() string { return "ListValue" } + +func (m *ListValue) GetValues() []*Value { + if m != nil { + return m.Values + } + return nil +} + +func init() { + proto.RegisterType((*Struct)(nil), "google.protobuf.Struct") + proto.RegisterType((*Value)(nil), "google.protobuf.Value") + proto.RegisterType((*ListValue)(nil), "google.protobuf.ListValue") + proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value) +} + +func init() { proto.RegisterFile("google/protobuf/struct.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 417 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8b, 0xd3, 0x40, + 0x14, 0xc7, 0x3b, 0xc9, 0x36, 0x98, 0x17, 0x59, 0x97, 0x11, 0xb4, 0xac, 0xa2, 0xa1, 0x7b, 0x09, + 0x22, 0x29, 0xd6, 0x8b, 0x18, 0x2f, 0x06, 0xd6, 0x5d, 0x30, 0x2c, 0x31, 0xba, 0x15, 0xbc, 0x94, + 0x26, 0x4d, 0x63, 0xe8, 0x74, 0x26, 0x24, 0x33, 0x4a, 0x8f, 0x7e, 0x0b, 0xcf, 0x1e, 0x3d, 0xfa, + 0xe9, 0x3c, 0xca, 0xcc, 0x24, 0xa9, 0xb4, 0xf4, 0x94, 0xbc, 0xf7, 0x7e, 0xef, 0x3f, 0xef, 0xff, + 0x66, 0xe0, 0x71, 0xc1, 0x58, 0x41, 0xf2, 0x49, 0x55, 0x33, 0xce, 0x52, 0xb1, 0x9a, 0x34, 0xbc, + 0x16, 0x19, 0xf7, 0x55, 0x8c, 0xef, 0xe9, 0xaa, 0xdf, 0x55, 0xc7, 0x3f, 0x11, 0x58, 0x1f, 0x15, + 0x81, 0x03, 0xb0, 0x56, 0x65, 0x4e, 0x96, 0xcd, 0x08, 0xb9, 0xa6, 0xe7, 0x4c, 0x2f, 0xfc, 0x3d, + 0xd8, 0xd7, 0xa0, 0xff, 0x4e, 0x51, 0x97, 0x94, 0xd7, 0xdb, 0xa4, 0x6d, 0x39, 0xff, 0x00, 0xce, + 0x7f, 0x69, 0x7c, 0x06, 0xe6, 0x3a, 0xdf, 0x8e, 0x90, 0x8b, 0x3c, 0x3b, 0x91, 0xbf, 0xf8, 0x39, + 0x0c, 0xbf, 0x2d, 0x88, 0xc8, 0x47, 0x86, 0x8b, 0x3c, 0x67, 0xfa, 0xe0, 0x40, 0x7c, 0x26, 0xab, + 0x89, 0x86, 0x5e, 0x1b, 0xaf, 0xd0, 0xf8, 0x8f, 0x01, 0x43, 0x95, 0xc4, 0x01, 0x00, 0x15, 0x84, + 0xcc, 0xb5, 0x80, 0x14, 0x3d, 0x9d, 0x9e, 0x1f, 0x08, 0xdc, 0x08, 0x42, 0x14, 0x7f, 0x3d, 0x48, + 0x6c, 0xda, 0x05, 0xf8, 0x02, 0xee, 0x52, 0xb1, 0x49, 0xf3, 0x7a, 0xbe, 0x3b, 0x1f, 0x5d, 0x0f, + 0x12, 0x47, 0x67, 0x7b, 0xa8, 0xe1, 0x75, 0x49, 0x8b, 0x16, 0x32, 0xe5, 0xe0, 0x12, 0xd2, 0x59, + 0x0d, 0x3d, 0x05, 0x48, 0x19, 0xeb, 0xc6, 0x38, 0x71, 0x91, 0x77, 0x47, 0x1e, 0x25, 0x73, 0x1a, + 0x78, 0xa3, 0x54, 0x44, 0xc6, 0x5b, 0x64, 0xa8, 0xac, 0x3e, 0x3c, 0xb2, 0xc7, 0x56, 0x5e, 0x64, + 0xbc, 0x77, 0x49, 0xca, 0xa6, 0xeb, 0xb5, 0x54, 0xef, 0xa1, 0xcb, 0xa8, 0x6c, 0x78, 0xef, 0x92, + 0x74, 0x41, 0x68, 0xc1, 0xc9, 0xba, 0xa4, 0xcb, 0x71, 0x00, 0x76, 0x4f, 0x60, 0x1f, 0x2c, 0x25, + 0xd6, 0xdd, 0xe8, 0xb1, 0xa5, 0xb7, 0xd4, 0xb3, 0x47, 0x60, 0xf7, 0x4b, 0xc4, 0xa7, 0x00, 0x37, + 0xb7, 0x51, 0x34, 0x9f, 0xbd, 0x8d, 0x6e, 0x2f, 0xcf, 0x06, 0xe1, 0x0f, 0x04, 0xf7, 0x33, 0xb6, + 0xd9, 0x97, 0x08, 0x1d, 0xed, 0x26, 0x96, 0x71, 0x8c, 0xbe, 0xbc, 0x28, 0x4a, 0xfe, 0x55, 0xa4, + 0x7e, 0xc6, 0x36, 0x93, 0x82, 0x91, 0x05, 0x2d, 0x76, 0x4f, 0xb1, 0xe2, 0xdb, 0x2a, 0x6f, 0xda, + 0x17, 0x19, 0xe8, 0x4f, 0x95, 0xfe, 0x45, 0xe8, 0x97, 0x61, 0x5e, 0xc5, 0xe1, 0x6f, 0xe3, 0xc9, + 0x95, 0x16, 0x8f, 0xbb, 0xf9, 0x3e, 0xe7, 0x84, 0xbc, 0xa7, 0xec, 0x3b, 0xfd, 0x24, 0x3b, 0x53, + 0x4b, 0x49, 0xbd, 0xfc, 0x17, 0x00, 0x00, 0xff, 0xff, 0xe8, 0x1b, 0x59, 0xf8, 0xe5, 0x02, 0x00, + 0x00, +} diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto b/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto new file mode 100644 index 0000000..7d7808e --- /dev/null +++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.proto @@ -0,0 +1,96 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/struct;structpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "StructProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +message Struct { + // Unordered map of dynamically typed values. + map fields = 1; +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of that +// variants, absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +message Value { + // The kind of value. + oneof kind { + // Represents a null value. + NullValue null_value = 1; + // Represents a double value. + double number_value = 2; + // Represents a string value. + string string_value = 3; + // Represents a boolean value. + bool bool_value = 4; + // Represents a structured value. + Struct struct_value = 5; + // Represents a repeated `Value`. + ListValue list_value = 6; + } +} + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +enum NullValue { + // Null value. + NULL_VALUE = 0; +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +message ListValue { + // Repeated field of dynamically typed values. + repeated Value values = 1; +} diff --git a/vendor/github.com/twitchtv/twirp/.gitignore b/vendor/github.com/twitchtv/twirp/.gitignore new file mode 100644 index 0000000..1281c22 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/.gitignore @@ -0,0 +1,7 @@ +*.test +/bin + +clientcompat/pycompat/ENV +*.pyc + +build diff --git a/vendor/github.com/twitchtv/twirp/CONTRIBUTING.md b/vendor/github.com/twitchtv/twirp/CONTRIBUTING.md new file mode 100644 index 0000000..39eea58 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# Contributing # + +Thanks for helping make Twirp better! This is great! + +First, if you have run into a bug, please file an issue. We try to get back to +issue reporters within a day or two. We might be able to help you right away. + +If you'd rather not publicly discuss the issue, please email spencer@twitch.tv +and/or security@twitch.tv. + +Issues are also a good place to present experience reports or requests for new +features. + +If you'd like to make changes to Twirp, read on: + +## Setup Requirements ## + +You will need git, Go 1.9+, and Python 2.7 installed and on your system's path. +Install them however you feel. + +We work on a branch called `develop`. We periodically release this branch as a +new version, then accumulate more changes on the develop branch until the next +release. Use `develop` as the base for your branches. + +## Developer Loop ## + +Generally you want to make changes and run `make`, which will install all +dependencies we know about, build the core, and run all of the tests that we +have against all of the languages we support. + +Most tests of the Go server are in `internal/twirptest/service_test.go`. Tests +of cross-language clients are in the [clientcompat](./clientcompat) directory. + +## Contributing Code ## + +Twirp uses github pull requests. Fork a branch from `develop`, hack away at your +changes, run the test suite with `make`, and submit a PR. + +## Releasing Versions ## + +Releasing versions is the responsibility of the core maintainers. Most people +don't need to know this stuff. + +Twirp uses [Semantic versioning](http://semver.org/): `v..`. + + * Increment major if you're making a backwards-incompatible change. + * Increment minor if you're adding a feature that's backwards-compatible. + * Increment patch if you're making a bugfix. + +To make a release, remember to update the version number in +[internal/gen/version.go](./internal/gen/version.go). + +Twirp uses Github releases. To make a new release: + 1. Merge all changes that should be included in the release into the master + branch. + 2. Add a new commit to master with a message like "Version vX.X.X release". + 3. Tag the commit you just made: `git tag ` and `git push + origin --tags` + 3. Go to Github https://github.com/twitchtv/twirp/releases and + "Draft a new release". + 4. Make sure to document changes, specially when upgrade instructions are + needed. diff --git a/vendor/github.com/twitchtv/twirp/LICENSE b/vendor/github.com/twitchtv/twirp/LICENSE new file mode 100644 index 0000000..a50e576 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 Twitch Interactive, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/twitchtv/twirp/Makefile b/vendor/github.com/twitchtv/twirp/Makefile new file mode 100644 index 0000000..efaa94c --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/Makefile @@ -0,0 +1,51 @@ +RETOOL=$(CURDIR)/_tools/bin/retool +PATH := ${PWD}/bin:${PWD}/ENV/bin:${PATH} +.DEFAULT_GOAL := all + +all: setup test_all + +.PHONY: test test_all test_core test_clients test_go_client test_python_client generate + +# Phony commands: +generate: + PATH=$(CURDIR)/_tools/bin:$(PATH) GOBIN="${PWD}/bin" go install -v ./protoc-gen-... + $(RETOOL) do go generate ./... + +test_all: setup test_core test_clients + +test_core: generate + $(RETOOL) do errcheck -blank ./internal/twirptest + go test -race $(shell go list ./... | grep -v /vendor/ | grep -v /_tools/) + +test_clients: test_go_client test_python_client + +test_go_client: generate build/clientcompat build/gocompat + ./build/clientcompat -client ./build/gocompat + +test_python_client: generate build/clientcompat build/pycompat + ./build/clientcompat -client ./build/pycompat + +setup: + ./install_proto.bash + GOPATH=$(CURDIR)/_tools go install github.com/twitchtv/retool/... + $(RETOOL) build + +# Actual files for testing clients: +./build: + mkdir build + +./build/gocompat: ./build + go build -o build/gocompat ./clientcompat/gocompat + +./build/clientcompat: ./build + go build -o build/clientcompat ./clientcompat + +./build/venv: ./build + virtualenv ./build/venv + +./build/venv/bin/pycompat.py: ./build/venv + ./build/venv/bin/pip install --upgrade ./clientcompat/pycompat + +./build/pycompat: ./build/venv/bin/pycompat.py + cp ./clientcompat/pycompat/pycompat.sh ./build/pycompat + chmod +x ./build/pycompat diff --git a/vendor/github.com/twitchtv/twirp/README.md b/vendor/github.com/twitchtv/twirp/README.md new file mode 100644 index 0000000..aec0d5d --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/README.md @@ -0,0 +1,34 @@ +![Twirp Logo](./logo.png) + +--- + +Twirp is a framework for service-to-service communication emphasizing simplicity +and minimalism. It generates routing and serialization from API definition files +and lets you focus on your application's logic instead of thinking about +folderol like HTTP methods and paths and JSON. + +Define your service in a +[Protobuf](https://developers.google.com/protocol-buffers/docs/proto3) file and +then Twirp autogenerates Go code with a server interface and fully functional +clients. It's similar to [gRPC](http://www.grpc.io/), but without the custom +HTTP server and transport implementations: it runs on the standard library's +extremely-well-tested-and-high-performance `net/http` Server. It can run on HTTP +1.1, not just http/2, and supports JSON clients for easy integrations across +languages + +Twirp handles routing and serialization for you in a well-tested, standardized, +thoughtful way so you don't have to. Serialization and deserialization code is +error-prone and tricky, and you shouldn't be wasting your time deciding whether +it should be "POST /friends/:id/new" or "POST /:id/friend" or whatever. Just +get to the real work of building services! + +Along the way, you get an autogenerated client and a simple, smart framework for +passing error messages. Nice! + +### Releases +Twirp follows semantic versioning through git tags, and uses Github Releases for +release notes and upgrade guides: +[Twirp Releases](https://github.com/twitchtv/twirp/releases) + +### Contributing +Check out [CONTRIBUTING.md](./CONTRIBUTING.md) for notes on making contributions. diff --git a/vendor/github.com/twitchtv/twirp/context.go b/vendor/github.com/twitchtv/twirp/context.go new file mode 100644 index 0000000..e65331e --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/context.go @@ -0,0 +1,109 @@ +package twirp + +import ( + "context" + "errors" + "net/http" + + "github.com/twitchtv/twirp/internal/contextkeys" +) + +// MethodName extracts the name of the method being handled in the given +// context. If it is not known, it returns ("", false). +func MethodName(ctx context.Context) (string, bool) { + name, ok := ctx.Value(contextkeys.MethodNameKey).(string) + return name, ok +} + +// ServiceName extracts the name of the service handling the given context. If +// it is not known, it returns ("", false). +func ServiceName(ctx context.Context) (string, bool) { + name, ok := ctx.Value(contextkeys.ServiceNameKey).(string) + return name, ok +} + +// PackageName extracts the fully-qualified protobuf package name of the service +// handling the given context. If it is not known, it returns ("", false). If +// the service comes from a proto file that does not declare a package name, it +// returns ("", true). +// +// Note that the protobuf package name can be very different than the go package +// name; the two are unrelated. +func PackageName(ctx context.Context) (string, bool) { + name, ok := ctx.Value(contextkeys.PackageNameKey).(string) + return name, ok +} + +// StatusCode retrieves the status code of the response (as string like "200"). +// If it is known returns (status, true). +// If it is not known, it returns ("", false). +func StatusCode(ctx context.Context) (string, bool) { + code, ok := ctx.Value(contextkeys.StatusCodeKey).(string) + return code, ok +} + +// WithHTTPRequestHeaders stores an http.Header in a context.Context. When +// using a Twirp-generated client, you can pass the returned context +// into any of the request methods, and the stored header will be +// included in outbound HTTP requests. +// +// This can be used to set custom HTTP headers like authorization tokens or +// client IDs. But note that HTTP headers are a Twirp implementation detail, +// only visible by middleware, not by the server implementtion. +// +// WithHTTPRequestHeaders returns an error if the provided http.Header +// would overwrite a header that is needed by Twirp, like "Content-Type". +func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error) { + if _, ok := h["Content-Type"]; ok { + return nil, errors.New("provided header cannot set Content-Type") + } + if _, ok := h["Twirp-Version"]; ok { + return nil, errors.New("provided header cannot set Twirp-Version") + } + + copied := make(http.Header, len(h)) + for k, vv := range h { + if vv == nil { + copied[k] = nil + continue + } + copied[k] = make([]string, len(vv)) + copy(copied[k], vv) + } + + return context.WithValue(ctx, contextkeys.RequestHeaderKey, copied), nil +} + +func HTTPRequestHeaders(ctx context.Context) (http.Header, bool) { + h, ok := ctx.Value(contextkeys.RequestHeaderKey).(http.Header) + return h, ok +} + +// SetHTTPResponseHeader sets an HTTP header key-value pair using a context +// provided by a twirp-generated server, or a child of that context. +// The server will include the header in its response for that request context. +// +// This can be used to respond with custom HTTP headers like "Cache-Control". +// But note that HTTP headers are a Twirp implementation detail, +// only visible by middleware, not by the clients or their responses. +// +// The header will be ignored (noop) if the context is invalid (i.e. using a new +// context.Background() instead of passing the context from the handler). +// +// If called multiple times with the same key, it replaces any existing values +// associated with that key. +// +// SetHTTPResponseHeader returns an error if the provided header key +// would overwrite a header that is needed by Twirp, like "Content-Type". +func SetHTTPResponseHeader(ctx context.Context, key, value string) error { + if key == "Content-Type" { + return errors.New("header key can not be Content-Type") + } + + responseWriter, ok := ctx.Value(contextkeys.ResponseWriterKey).(http.ResponseWriter) + if ok { + responseWriter.Header().Set(key, value) + } // invalid context is ignored, not an error, this is to allow easy unit testing with mock servers + + return nil +} diff --git a/vendor/github.com/twitchtv/twirp/ctxsetters/ctxsetters.go b/vendor/github.com/twitchtv/twirp/ctxsetters/ctxsetters.go new file mode 100644 index 0000000..b4c23ac --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/ctxsetters/ctxsetters.go @@ -0,0 +1,34 @@ +// Package ctxsetters is an implementation detail for twirp generated code, used +// by the generated servers to set values in contexts for later access with the +// twirp package's accessors. +// +// Do not use ctxsetters outside of twirp's generated code. +package ctxsetters + +import ( + "context" + "net/http" + "strconv" + + "github.com/twitchtv/twirp/internal/contextkeys" +) + +func WithMethodName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, contextkeys.MethodNameKey, name) +} + +func WithServiceName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, contextkeys.ServiceNameKey, name) +} + +func WithPackageName(ctx context.Context, name string) context.Context { + return context.WithValue(ctx, contextkeys.PackageNameKey, name) +} + +func WithStatusCode(ctx context.Context, code int) context.Context { + return context.WithValue(ctx, contextkeys.StatusCodeKey, strconv.Itoa(code)) +} + +func WithResponseWriter(ctx context.Context, w http.ResponseWriter) context.Context { + return context.WithValue(ctx, contextkeys.ResponseWriterKey, w) +} diff --git a/vendor/github.com/twitchtv/twirp/errors.go b/vendor/github.com/twitchtv/twirp/errors.go new file mode 100644 index 0000000..5b50ef5 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/errors.go @@ -0,0 +1,328 @@ +// package twirp provides core types used in generated Twirp servers and client. +// +// Twirp services handle errors using the `twirp.Error` interface. +// +// For example, a server method may return an InvalidArgumentError: +// +// if req.Order != "DESC" && req.Order != "ASC" { +// return nil, twirp.InvalidArgumentError("Order", "must be DESC or ASC") +// } +// +// And the same twirp.Error is returned by the client, for example: +// +// resp, err := twirpClient.RPCMethod(ctx, req) +// if err != nil { +// if twerr := err.(twirp.Error) { +// switch twerr.Code() { +// case twirp.InvalidArgument: +// log.Error("invalid argument "+twirp.Meta("argument")) +// default: +// log.Error(twerr.Error()) +// } +// } +// } +// +// Clients may also return Internal errors if something failed on the system: +// the server, the network, or the client itself (i.e. failure parsing +// response). +// +package twirp + +import "fmt" + +// Error represents an error in a Twirp service call. +type Error interface { + // Code is of the valid error codes. + Code() ErrorCode + + // Msg returns a human-readable, unstructured messages describing the error. + Msg() string + + // WithMeta returns a copy of the Error with the given key-value pair attached + // as metadata. If the key is already set, it is overwritten. + WithMeta(key string, val string) Error + + // Meta returns the stored value for the given key. If the key has no set + // value, Meta returns an empty string. There is no way to distinguish between + // an unset value and an explicit empty string. + Meta(key string) string + + // MetaMap returns the complete key-value metadata map stored on the error. + MetaMap() map[string]string + + // Error returns a string of the form "twirp error : " + Error() string +} + +// NewError is the generic constructor for a twirp.Error. The ErrorCode must be +// one of the valid predefined constants, otherwise it will be converted to an +// error {type: Internal, msg: "invalid error type {{code}}"}. If you need to +// add metadata, use .WithMeta(key, value) method after building the error. +func NewError(code ErrorCode, msg string) Error { + if IsValidErrorCode(code) { + return &twerr{ + code: code, + msg: msg, + } + } + return &twerr{ + code: Internal, + msg: "invalid error type " + string(code), + } +} + +// NotFoundError constructor for the common NotFound error. +func NotFoundError(msg string) Error { + return NewError(NotFound, msg) +} + +// InvalidArgumentError constructor for the common InvalidArgument error. Can be +// used when an argument has invalid format, is a number out of range, is a bad +// option, etc). +func InvalidArgumentError(argument string, validationMsg string) Error { + err := NewError(InvalidArgument, argument+" "+validationMsg) + err = err.WithMeta("argument", argument) + return err +} + +// RequiredArgumentError is a more scpecific constructor for InvalidArgument +// error. Should be used when the argument is required (expected to have a +// non-zero value). +func RequiredArgumentError(argument string) Error { + return InvalidArgumentError(argument, "is required") +} + +// InternalError constructor for the common Internal error. Should be used to +// specify that something bad or unexpected happened. +func InternalError(msg string) Error { + return NewError(Internal, msg) +} + +// InternalErrorWith is an easy way to wrap another error. It adds the +// underlying error's type as metadata with a key of "cause", which can be +// useful for debugging. Should be used in the common case of an unexpected +// error returned from another API, but sometimes it is better to build a more +// specific error (like with NewError(Unknown, err.Error()), for example). +// +// The returned error also has a Cause() method which will return the original +// error, if it is known. This can be used with the github.com/pkg/errors +// package to extract the root cause of an error. Information about the root +// cause of an error is lost when it is serialized, so this doesn't let a client +// know the exact root cause of a server's error. +func InternalErrorWith(err error) Error { + msg := err.Error() + twerr := NewError(Internal, msg) + twerr = twerr.WithMeta("cause", fmt.Sprintf("%T", err)) // to easily tell apart wrapped internal errors from explicit ones + return &wrappedErr{ + wrapper: twerr, + cause: err, + } +} + +// ErrorCode represents a Twirp error type. +type ErrorCode string + +// Valid Twirp error types. Most error types are equivalent to GRPC status codes +// and follow the same semantics. +const ( + // Canceled indicates the operation was cancelled (typically by the caller). + Canceled ErrorCode = "canceled" + + // Unknown error. For example when handling errors raised by APIs that do not + // return enough error information. + Unknown ErrorCode = "unknown" + + // InvalidArgument indicates client specified an invalid argument. It + // indicates arguments that are problematic regardless of the state of the + // system (i.e. a malformed file name, required argument, number out of range, + // etc.). + InvalidArgument ErrorCode = "invalid_argument" + + // DeadlineExceeded means operation expired before completion. For operations + // that change the state of the system, this error may be returned even if the + // operation has completed successfully (timeout). + DeadlineExceeded ErrorCode = "deadline_exceeded" + + // NotFound means some requested entity was not found. + NotFound ErrorCode = "not_found" + + // BadRoute means that the requested URL path wasn't routable to a Twirp + // service and method. This is returned by the generated server, and usually + // shouldn't be returned by applications. Instead, applications should use + // NotFound or Unimplemented. + BadRoute ErrorCode = "bad_route" + + // AlreadyExists means an attempt to create an entity failed because one + // already exists. + AlreadyExists ErrorCode = "already_exists" + + // PermissionDenied indicates the caller does not have permission to execute + // the specified operation. It must not be used if the caller cannot be + // identified (Unauthenticated). + PermissionDenied ErrorCode = "permission_denied" + + // Unauthenticated indicates the request does not have valid authentication + // credentials for the operation. + Unauthenticated ErrorCode = "unauthenticated" + + // ResourceExhausted indicates some resource has been exhausted, perhaps a + // per-user quota, or perhaps the entire file system is out of space. + ResourceExhausted ErrorCode = "resource_exhausted" + + // FailedPrecondition indicates operation was rejected because the system is + // not in a state required for the operation's execution. For example, doing + // an rmdir operation on a directory that is non-empty, or on a non-directory + // object, or when having conflicting read-modify-write on the same resource. + FailedPrecondition ErrorCode = "failed_precondition" + + // Aborted indicates the operation was aborted, typically due to a concurrency + // issue like sequencer check failures, transaction aborts, etc. + Aborted ErrorCode = "aborted" + + // OutOfRange means operation was attempted past the valid range. For example, + // seeking or reading past end of a paginated collection. + // + // Unlike InvalidArgument, this error indicates a problem that may be fixed if + // the system state changes (i.e. adding more items to the collection). + // + // There is a fair bit of overlap between FailedPrecondition and OutOfRange. + // We recommend using OutOfRange (the more specific error) when it applies so + // that callers who are iterating through a space can easily look for an + // OutOfRange error to detect when they are done. + OutOfRange ErrorCode = "out_of_range" + + // Unimplemented indicates operation is not implemented or not + // supported/enabled in this service. + Unimplemented ErrorCode = "unimplemented" + + // Internal errors. When some invariants expected by the underlying system + // have been broken. In other words, something bad happened in the library or + // backend service. Do not confuse with HTTP Internal Server Error; an + // Internal error could also happen on the client code, i.e. when parsing a + // server response. + Internal ErrorCode = "internal" + + // Unavailable indicates the service is currently unavailable. This is a most + // likely a transient condition and may be corrected by retrying with a + // backoff. + Unavailable ErrorCode = "unavailable" + + // DataLoss indicates unrecoverable data loss or corruption. + DataLoss ErrorCode = "data_loss" + + // NoError is the zero-value, is considered an empty error and should not be + // used. + NoError ErrorCode = "" +) + +// ServerHTTPStatusFromErrorCode maps a Twirp error type into a similar HTTP +// response status. It is used by the Twirp server handler to set the HTTP +// response status code. Returns 0 if the ErrorCode is invalid. +func ServerHTTPStatusFromErrorCode(code ErrorCode) int { + switch code { + case Canceled: + return 408 // RequestTimeout + case Unknown: + return 500 // Internal Server Error + case InvalidArgument: + return 400 // BadRequest + case DeadlineExceeded: + return 408 // RequestTimeout + case NotFound: + return 404 // Not Found + case BadRoute: + return 404 // Not Found + case AlreadyExists: + return 409 // Conflict + case PermissionDenied: + return 403 // Forbidden + case Unauthenticated: + return 401 // Unauthorized + case ResourceExhausted: + return 403 // Forbidden + case FailedPrecondition: + return 412 // Precondition Failed + case Aborted: + return 409 // Conflict + case OutOfRange: + return 400 // Bad Request + case Unimplemented: + return 501 // Not Implemented + case Internal: + return 500 // Internal Server Error + case Unavailable: + return 503 // Service Unavailable + case DataLoss: + return 500 // Internal Server Error + case NoError: + return 200 // OK + default: + return 0 // Invalid! + } +} + +// IsValidErrorCode returns true if is one of the valid predefined constants. +func IsValidErrorCode(code ErrorCode) bool { + return ServerHTTPStatusFromErrorCode(code) != 0 +} + +// twirp.Error implementation +type twerr struct { + code ErrorCode + msg string + meta map[string]string +} + +func (e *twerr) Code() ErrorCode { return e.code } +func (e *twerr) Msg() string { return e.msg } + +func (e *twerr) Meta(key string) string { + if e.meta != nil { + return e.meta[key] // also returns "" if key is not in meta map + } + return "" +} + +func (e *twerr) WithMeta(key string, value string) Error { + newErr := &twerr{ + code: e.code, + msg: e.msg, + meta: make(map[string]string, len(e.meta)), + } + for k, v := range e.meta { + newErr.meta[k] = v + } + newErr.meta[key] = value + return newErr +} + +func (e *twerr) MetaMap() map[string]string { + return e.meta +} + +func (e *twerr) Error() string { + return fmt.Sprintf("twirp error %s: %s", e.code, e.msg) +} + +// wrappedErr fulfills the twirp.Error interface and the +// github.com/pkg/errors.Causer interface. It exposes all the twirp error +// methods, but root cause of an error can be retrieved with +// (*wrappedErr).Cause. This is expected to be used with the InternalErrorWith +// function. +type wrappedErr struct { + wrapper Error + cause error +} + +func (e *wrappedErr) Code() ErrorCode { return e.wrapper.Code() } +func (e *wrappedErr) Msg() string { return e.wrapper.Msg() } +func (e *wrappedErr) Meta(key string) string { return e.wrapper.Meta(key) } +func (e *wrappedErr) MetaMap() map[string]string { return e.wrapper.MetaMap() } +func (e *wrappedErr) Error() string { return e.wrapper.Error() } +func (e *wrappedErr) WithMeta(key string, val string) Error { + return &wrappedErr{ + wrapper: e.wrapper.WithMeta(key, val), + cause: e.cause, + } +} +func (e *wrappedErr) Cause() error { return e.cause } diff --git a/vendor/github.com/twitchtv/twirp/errors_test.go b/vendor/github.com/twitchtv/twirp/errors_test.go new file mode 100644 index 0000000..3c9365e --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/errors_test.go @@ -0,0 +1,38 @@ +package twirp + +import ( + "fmt" + "sync" + "testing" + + "github.com/pkg/errors" +) + +func TestWithMetaRaces(t *testing.T) { + err := NewError(Internal, "msg") + err = err.WithMeta("k1", "v1") + + var wg sync.WaitGroup + for i := 0; i < 1000; i++ { + wg.Add(1) + go func(i int) { + _ = err.WithMeta(fmt.Sprintf("k-%d", i), "v") + wg.Done() + }(i) + } + + wg.Wait() + + if len(err.MetaMap()) != 1 { + t.Errorf("err was mutated") + } +} + +func TestErrorCause(t *testing.T) { + rootCause := fmt.Errorf("this is only a test") + twerr := InternalErrorWith(rootCause) + cause := errors.Cause(twerr) + if cause != rootCause { + t.Errorf("got wrong cause for err. have=%q, want=%q", cause, rootCause) + } +} diff --git a/vendor/github.com/twitchtv/twirp/hooks.go b/vendor/github.com/twitchtv/twirp/hooks.go new file mode 100644 index 0000000..0107f1e --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/hooks.go @@ -0,0 +1,108 @@ +package twirp + +import "context" + +// ServerHooks is a container for callbacks that can instrument a +// Twirp-generated server. These callbacks all accept a context and return a +// context. They can use this to add to the request context as it threads +// through the system, appending values or deadlines to it. +// +// The RequestReceived and RequestRouted hooks are special: they can return +// errors. If they return a non-nil error, handling for that request will be +// stopped at that point. The Error hook will be triggered, and the error will +// be sent to the client. This can be used for stuff like auth checks before +// deserializing a request. +// +// The RequestReceived hook is always called first, and it is called for every +// request that the Twirp server handles. The last hook to be called in a +// request's lifecycle is always ResponseSent, even in the case of an error. +// +// Details on the timing of each hook are documented as comments on the fields +// of the ServerHooks type. +type ServerHooks struct { + // RequestReceived is called as soon as a request enters the Twirp + // server at the earliest available moment. + RequestReceived func(context.Context) (context.Context, error) + + // RequestRouted is called when a request has been routed to a + // particular method of the Twirp server. + RequestRouted func(context.Context) (context.Context, error) + + // ResponsePrepared is called when a request has been handled and a + // response is ready to be sent to the client. + ResponsePrepared func(context.Context) context.Context + + // ResponseSent is called when all bytes of a response (including an error + // response) have been written. Because the ResponseSent hook is terminal, it + // does not return a context. + ResponseSent func(context.Context) + + // Error hook is called when a request responds with an Error, + // either by the service implementation or by Twirp itself. + // The Error is passed as argument to the hook. + Error func(context.Context, Error) context.Context +} + +// ChainHooks creates a new *ServerHooks which chains the callbacks in +// each of the constituent hooks passed in. Each hook function will be +// called in the order of the ServerHooks values passed in. +// +// For the erroring hooks, RequestReceived and RequestRouted, any returned +// errors prevent processing by later hooks. +func ChainHooks(hooks ...*ServerHooks) *ServerHooks { + if len(hooks) == 0 { + return nil + } + if len(hooks) == 1 { + return hooks[0] + } + return &ServerHooks{ + RequestReceived: func(ctx context.Context) (context.Context, error) { + var err error + for _, h := range hooks { + if h != nil && h.RequestReceived != nil { + ctx, err = h.RequestReceived(ctx) + if err != nil { + return ctx, err + } + } + } + return ctx, nil + }, + RequestRouted: func(ctx context.Context) (context.Context, error) { + var err error + for _, h := range hooks { + if h != nil && h.RequestRouted != nil { + ctx, err = h.RequestRouted(ctx) + if err != nil { + return ctx, err + } + } + } + return ctx, nil + }, + ResponsePrepared: func(ctx context.Context) context.Context { + for _, h := range hooks { + if h != nil && h.ResponsePrepared != nil { + ctx = h.ResponsePrepared(ctx) + } + } + return ctx + }, + ResponseSent: func(ctx context.Context) { + for _, h := range hooks { + if h != nil && h.ResponseSent != nil { + h.ResponseSent(ctx) + } + } + }, + Error: func(ctx context.Context, twerr Error) context.Context { + for _, h := range hooks { + if h != nil && h.Error != nil { + ctx = h.Error(ctx, twerr) + } + } + return ctx + }, + } +} diff --git a/vendor/github.com/twitchtv/twirp/hooks_test.go b/vendor/github.com/twitchtv/twirp/hooks_test.go new file mode 100644 index 0000000..3cc7c42 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/hooks_test.go @@ -0,0 +1,75 @@ +package twirp + +import ( + "context" + "reflect" + "testing" +) + +func TestChainHooks(t *testing.T) { + var ( + hook1 = new(ServerHooks) + hook2 = new(ServerHooks) + hook3 = new(ServerHooks) + ) + + const key = "key" + + hook1.RequestReceived = func(ctx context.Context) (context.Context, error) { + return context.WithValue(ctx, key, []string{"hook1"}), nil + } + hook2.RequestReceived = func(ctx context.Context) (context.Context, error) { + v := ctx.Value(key).([]string) + return context.WithValue(ctx, key, append(v, "hook2")), nil + } + hook3.RequestReceived = func(ctx context.Context) (context.Context, error) { + v := ctx.Value(key).([]string) + return context.WithValue(ctx, key, append(v, "hook3")), nil + } + + hook1.RequestRouted = func(ctx context.Context) (context.Context, error) { + return context.WithValue(ctx, key, []string{"hook1"}), nil + } + + hook2.ResponsePrepared = func(ctx context.Context) context.Context { + return context.WithValue(ctx, key, []string{"hook2"}) + } + + chain := ChainHooks(hook1, hook2, hook3) + + ctx := context.Background() + + // When all three chained hooks have a handler, all should be called in order. + want := []string{"hook1", "hook2", "hook3"} + haveCtx, err := chain.RequestReceived(ctx) + if err != nil { + t.Fatalf("RequestReceived chain has unexpected err %v", err) + } + have := haveCtx.Value(key) + if !reflect.DeepEqual(want, have) { + t.Errorf("RequestReceived chain has unexpected ctx, have=%v, want=%v", have, want) + } + + // When only the first chained hook has a handler, it should be called, and + // there should be no panic. + want = []string{"hook1"} + haveCtx, err = chain.RequestRouted(ctx) + if err != nil { + t.Fatalf("RequestRouted chain has unexpected err %v", err) + } + have = haveCtx.Value(key) + if !reflect.DeepEqual(want, have) { + t.Errorf("RequestRouted chain has unexpected ctx, have=%v, want=%v", have, want) + } + + // When only the second chained hook has a handler, it should be called, and + // there should be no panic. + want = []string{"hook2"} + have = chain.ResponsePrepared(ctx).Value(key) + if !reflect.DeepEqual(want, have) { + t.Errorf("RequestRouted chain has unexpected ctx, have=%v, want=%v", have, want) + } + + // When none of the chained hooks has a handler there should be no panic. + chain.ResponseSent(ctx) +} diff --git a/vendor/github.com/twitchtv/twirp/install_proto.bash b/vendor/github.com/twitchtv/twirp/install_proto.bash new file mode 100755 index 0000000..c46f11a --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/install_proto.bash @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +which protoc +PROTOC_EXISTS=$? +if [ $PROTOC_EXISTS -eq 0 ]; then + echo "Protoc already installed" + exit 0 +fi + +if [ "$(uname)" == "Darwin" ]; then + brew install protobuf +elif [ `whoami` == "root" ]; then + mkdir -p /usr/local/src/protoc + pushd /usr/local/src/protoc + wget https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip -O /usr/local/src/protoc-3.1.0-linux-x86_64.zip + unzip -x ../protoc-3.1.0-linux-x86_64.zip + if [ ! -e /usr/local/bin/protoc ]; then + ln -s `pwd`/bin/protoc /usr/local/bin/protoc + fi + popd +elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + echo "Project setup needs sudo to put protoc in /usr/local/src, so it will ask a few times" + sudo chmod a+w /usr/local/src + mkdir -p /usr/local/src/protoc + pushd /usr/local/src/protoc + wget https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip -O /usr/local/src/protoc-3.1.0-linux-x86_64.zip + unzip -x ../protoc-3.1.0-linux-x86_64.zip + if [ ! -e /usr/local/bin/protoc ]; then + sudo ln -s `pwd`/bin/protoc /usr/local/bin/protoc + fi + popd +fi +exit 0 \ No newline at end of file diff --git a/vendor/github.com/twitchtv/twirp/internal/contextkeys/keys.go b/vendor/github.com/twitchtv/twirp/internal/contextkeys/keys.go new file mode 100644 index 0000000..5c82c00 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/internal/contextkeys/keys.go @@ -0,0 +1,15 @@ +// Package contextkeys stores the keys to the context accessor +// functions, letting generated code safely set values in contexts +// without exposing the setters to the outside world. +package contextkeys + +type contextKey int + +const ( + MethodNameKey contextKey = 1 + iota + ServiceNameKey + PackageNameKey + StatusCodeKey + RequestHeaderKey + ResponseWriterKey +) diff --git a/vendor/github.com/twitchtv/twirp/internal/gogo_protoc_gen.sh b/vendor/github.com/twitchtv/twirp/internal/gogo_protoc_gen.sh new file mode 100755 index 0000000..f416b39 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/internal/gogo_protoc_gen.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# gogo_protoc_gen.sh foo.proto will compile foo.proto using +# github.com/gogo/protobuf/protoc-gen-gofast, an alternative generator used +# sometimes at Twitch.. Should be run in the same directory as its input. +# Handles multi-element GOPATHs so it works with retool. + +# Append '/src' to every element in GOPATH. +PROTOPATH=${GOPATH/://src:}/src + +protoc --proto_path="${PROTOPATH}:." --twirp_out=. --gofast_out=. "$@" diff --git a/vendor/github.com/twitchtv/twirp/internal/protoc_gen.sh b/vendor/github.com/twitchtv/twirp/internal/protoc_gen.sh new file mode 100755 index 0000000..1386082 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/internal/protoc_gen.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# protoc_gen.sh foo.proto will compile foo.proto. Should be run in the same +# directory as its input. Handles multi-element GOPATHs so it works with retool. + +# Append '/src' to every element in GOPATH. +PROTOPATH=${GOPATH/://src:}/src + +protoc --proto_path="${PROTOPATH}:." --twirp_out=. --go_out=. "$@" +protoc --proto_path="${PROTOPATH}:." --python_out=. --twirp_python_out=. "$@" diff --git a/vendor/github.com/twitchtv/twirp/logo.png b/vendor/github.com/twitchtv/twirp/logo.png new file mode 100644 index 0000000..c80f897 Binary files /dev/null and b/vendor/github.com/twitchtv/twirp/logo.png differ diff --git a/vendor/github.com/twitchtv/twirp/requirements.txt b/vendor/github.com/twitchtv/twirp/requirements.txt new file mode 100644 index 0000000..fb1bb8e --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/requirements.txt @@ -0,0 +1,7 @@ +certifi==2017.4.17 +chardet==3.0.4 +idna==2.5 +protobuf==3.3.0 +requests==2.17.3 +six==1.10.0 +urllib3==1.21.1 diff --git a/vendor/github.com/twitchtv/twirp/tools.json b/vendor/github.com/twitchtv/twirp/tools.json new file mode 100644 index 0000000..59cc1c4 --- /dev/null +++ b/vendor/github.com/twitchtv/twirp/tools.json @@ -0,0 +1,20 @@ +{ + "Tools": [ + { + "Repository": "github.com/golang/protobuf/protoc-gen-go", + "Commit": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317" + }, + { + "Repository": "github.com/kisielk/errcheck", + "Commit": "db0ca22445717d1b2c51ac1034440e0a2a2de645" + }, + { + "Repository": "github.com/gogo/protobuf/protoc-gen-gofast", + "Commit": "30433562cfbf487fe1df7cd26c7bab168d2f14d0" + }, + { + "Repository": "github.com/twitchtv/retool", + "Commit": "6f6d4930d88c40e23d2b54d12e64f0444e1fb4ef" + } + ] +} \ No newline at end of file