Merge pull request #75 from hasAdamr/context-to-transport-to-transport-to-context

Context to transport to transport to context
This commit is contained in:
Adam Ryman 2016-10-28 16:03:41 -07:00 коммит произвёл GitHub
Родитель 7b5e861316 6ff5dba7d1
Коммит 8c37fb79e0
11 изменённых файлов: 476 добавлений и 303 удалений

Просмотреть файл

@ -325,7 +325,6 @@ func applyTemplate(templBytes []byte, templName string, executor *templateExecut
templateString := string(templBytes)
codeTemplate, err := template.New(templName).Funcs(executor.funcMap).Parse(templateString)
if err != nil {
return nil, errors.Wrap(err, "could not create template")
}

Просмотреть файл

@ -24,6 +24,8 @@ import (
type Helper struct {
Methods []*Method
PathParamsBuilder string
ServerTemplate func(interface{}) (string, error)
ClientTemplate func(interface{}) (string, error)
}
// NewHelper builds a helper struct from a service declaration. The other
@ -35,6 +37,8 @@ func NewHelper(svc *deftree.ProtoService) *Helper {
pp := FormatCode(HTTPAssistFuncs)
rv := Helper{
PathParamsBuilder: pp,
ServerTemplate: GenServerTemplate,
ClientTemplate: GenClientTemplate,
}
for _, meth := range svc.Methods {
if len(meth.HttpBindings) > 0 {
@ -148,6 +152,24 @@ func NewBinding(i int, meth *deftree.ServiceMethod) *Binding {
return &nBinding
}
func GenServerTemplate(exec interface{}) (string, error) {
code, err := ApplyTemplate("ServerTemplate", serverTemplate, exec, TemplateFuncs)
if err != nil {
return "", err
}
code = FormatCode(code)
return code, nil
}
func GenClientTemplate(exec interface{}) (string, error) {
code, err := ApplyTemplate("ClientTemplate", clientTemplate, exec, TemplateFuncs)
if err != nil {
return "", err
}
code = FormatCode(code)
return code, nil
}
// GenServerDecode returns the generated code for the server-side decoding of
// an http request into its request struct.
func (b *Binding) GenServerDecode() (string, error) {

Просмотреть файл

@ -3,102 +3,102 @@ package httptransport
// ServerDecodeTemplate is the template for generating the server-side decoding
// function for a particular Binding.
var ServerDecodeTemplate = `
{{ with $binding := .}}
// DecodeHTTP{{$binding.Label}}Request is a transport/http.DecodeRequestFunc that
// decodes a JSON-encoded {{ToLower $binding.Parent.Name}} request from the HTTP request
// body. Primarily useful in a server.
func DecodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request) (interface{}, error) {
var req pb.{{GoName $binding.Parent.RequestType}}
err := json.NewDecoder(r.Body).Decode(&req)
// err = io.EOF if r.Body was empty
if err != nil && err != io.EOF {
return nil, errors.Wrap(err, "decoding body of http request")
}
{{- with $binding := . -}}
// DecodeHTTP{{$binding.Label}}Request is a transport/http.DecodeRequestFunc that
// decodes a JSON-encoded {{ToLower $binding.Parent.Name}} request from the HTTP request
// body. Primarily useful in a server.
func DecodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request) (interface{}, error) {
var req pb.{{GoName $binding.Parent.RequestType}}
err := json.NewDecoder(r.Body).Decode(&req)
// err = io.EOF if r.Body was empty
if err != nil && err != io.EOF {
return nil, errors.Wrap(err, "decoding body of http request")
}
pathParams, err := PathParams(r.URL.Path, "{{$binding.PathTemplate}}")
_ = pathParams
if err != nil {
fmt.Printf("Error while reading path params: %v\n", err)
return nil, errors.Wrap(err, "couldn't unmarshal path parameters")
pathParams, err := PathParams(r.URL.Path, "{{$binding.PathTemplate}}")
_ = pathParams
if err != nil {
fmt.Printf("Error while reading path params: %v\n", err)
return nil, errors.Wrap(err, "couldn't unmarshal path parameters")
}
queryParams, err := QueryParams(r.URL.Query())
_ = queryParams
if err != nil {
fmt.Printf("Error while reading query params: %v\n", err)
return nil, errors.Wrapf(err, "Error while reading query params: %v", r.URL.Query())
}
{{range $field := $binding.Fields}}
{{if ne $field.Location "body"}}
{{$field.GenQueryUnmarshaler}}
{{end}}
{{end}}
return &req, err
}
queryParams, err := QueryParams(r.URL.Query())
_ = queryParams
if err != nil {
fmt.Printf("Error while reading query params: %v\n", err)
return nil, errors.Wrapf(err, "Error while reading query params: %v", r.URL.Query())
}
{{range $field := $binding.Fields}}
{{if ne $field.Location "body"}}
{{$field.GenQueryUnmarshaler}}
{{end}}
{{end}}
return &req, err
}
{{end}}
{{- end -}}
`
// ClientEncodeTemplate is the template for generating the client-side encoding
// function for a particular Binding.
var ClientEncodeTemplate = `
{{ with $binding := .}}
// EncodeHTTP{{$binding.Label}}Request is a transport/http.EncodeRequestFunc
// that encodes a {{ToLower $binding.Parent.Name}} request into the various portions of
// the http request (path, query, and body).
func EncodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request, request interface{}) error {
fmt.Printf("Encoding request %v\n", request)
req := request.(*pb.{{GoName $binding.Parent.RequestType}})
_ = req
{{- with $binding := . -}}
// EncodeHTTP{{$binding.Label}}Request is a transport/http.EncodeRequestFunc
// that encodes a {{ToLower $binding.Parent.Name}} request into the various portions of
// the http request (path, query, and body).
func EncodeHTTP{{$binding.Label}}Request(_ context.Context, r *http.Request, request interface{}) error {
fmt.Printf("Encoding request %v\n", request)
req := request.(*pb.{{GoName $binding.Parent.RequestType}})
_ = req
// Set the path parameters
path := strings.Join([]string{
{{- range $section := $binding.PathSections}}
{{$section}},
{{- end}}
}, "/")
u, err := url.Parse(path)
if err != nil {
return errors.Wrapf(err, "couldn't unmarshal path %q", path)
}
r.URL.RawPath = u.RawPath
r.URL.Path = u.Path
// Set the query parameters
values := r.URL.Query()
var tmp []byte
_ = tmp
{{- range $field := $binding.Fields }}
{{- if eq $field.Location "query"}}
{{if or (not $field.IsBaseType) $field.Repeated}}
tmp, err = json.Marshal(req.{{$field.CamelName}})
// Set the path parameters
path := strings.Join([]string{
{{- range $section := $binding.PathSections}}
{{$section}},
{{- end}}
}, "/")
u, err := url.Parse(path)
if err != nil {
return errors.Wrap(err, "failed to marshal req.{{$field.CamelName}}")
return errors.Wrapf(err, "couldn't unmarshal path %q", path)
}
values.Add("{{$field.Name}}", string(tmp))
{{else}}
values.Add("{{$field.Name}}", fmt.Sprint(req.{{$field.CamelName}}))
{{- end }}
{{- end }}
{{- end}}
r.URL.RawPath = u.RawPath
r.URL.Path = u.Path
r.URL.RawQuery = values.Encode()
// Set the query parameters
values := r.URL.Query()
var tmp []byte
_ = tmp
{{- range $field := $binding.Fields }}
{{- if eq $field.Location "query"}}
{{if or (not $field.IsBaseType) $field.Repeated}}
tmp, err = json.Marshal(req.{{$field.CamelName}})
if err != nil {
return errors.Wrap(err, "failed to marshal req.{{$field.CamelName}}")
}
values.Add("{{$field.Name}}", string(tmp))
{{else}}
values.Add("{{$field.Name}}", fmt.Sprint(req.{{$field.CamelName}}))
{{- end }}
{{- end }}
{{- end}}
// Set the body parameters
var buf bytes.Buffer
toRet := map[string]interface{}{
{{- range $field := $binding.Fields -}}
{{if eq $field.Location "body"}}
"{{$field.CamelName}}" : req.{{$field.CamelName}},
{{end}}
r.URL.RawQuery = values.Encode()
// Set the body parameters
var buf bytes.Buffer
toRet := map[string]interface{}{
{{- range $field := $binding.Fields -}}
{{if eq $field.Location "body"}}
"{{$field.CamelName}}" : req.{{$field.CamelName}},
{{end}}
{{- end -}}
}
if err := json.NewEncoder(&buf).Encode(toRet); err != nil {
return errors.Wrapf(err, "couldn't encode body as json %v", toRet)
}
r.Body = ioutil.NopCloser(&buf)
fmt.Printf("URL: %v\n", r.URL)
return nil
}
{{- end -}}
}
if err := json.NewEncoder(&buf).Encode(toRet); err != nil {
return errors.Wrapf(err, "couldn't encode body as json %v", toRet)
}
r.Body = ioutil.NopCloser(&buf)
fmt.Printf("URL: %v\n", r.URL)
return nil
}
{{end}}
`
// WARNING: Changing the contents of these strings, even a little bit, will cause tests
@ -187,3 +187,255 @@ func QueryParams(vals url.Values) (map[string]string, error) {
// for encoding and decoding http request to and from generated protobuf
// structs, and is used within the generated code of each microservice.
var HTTPAssistFuncs = PathParamsTemplate + BuildParamMapTemplate + RemoveBracesTemplate + QueryParamsTemplate
var serverTemplate = `
package svc
// This file provides server-side bindings for the HTTP transport.
// It utilizes the transport/http.Server.
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"io"
"golang.org/x/net/context"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
httptransport "github.com/go-kit/kit/transport/http"
// This service
pb "{{.PBImportPath -}}"
)
var (
_ = fmt.Sprint
_ = bytes.Compare
_ = strconv.Atoi
_ = httptransport.NewServer
_ = ioutil.NopCloser
_ = pb.Register{{.Service.GetName}}Server
_ = io.Copy
)
// MakeHTTPHandler returns a handler that makes a set of endpoints available
// on predefined paths.
func MakeHTTPHandler(ctx context.Context, endpoints Endpoints, logger log.Logger) http.Handler {
serverOptions := []httptransport.ServerOption{
httptransport.ServerBefore(headersToContext),
}
m := http.NewServeMux()
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
m.Handle("{{ToLower $binding.BasePath}}", httptransport.NewServer(
ctx,
endpoints.{{$method.Name}}Endpoint,
HttpDecodeLogger(DecodeHTTP{{$binding.Label}}Request, logger),
EncodeHTTPGenericResponse,
serverOptions...,
))
{{- end}}
{{- end}}
return m
}
func HttpDecodeLogger(next httptransport.DecodeRequestFunc, logger log.Logger) httptransport.DecodeRequestFunc {
return func(ctx context.Context, r *http.Request) (interface{}, error) {
logger.Log("method", r.Method, "url", r.URL.String())
rv, err := next(ctx, r)
if err != nil {
logger.Log("method", r.Method, "url", r.URL.String(), "Error", err)
}
return rv, err
}
}
func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
code := http.StatusInternalServerError
msg := err.Error()
w.WriteHeader(code)
json.NewEncoder(w).Encode(errorWrapper{Error: msg})
}
func errorDecoder(r *http.Response) error {
var w errorWrapper
if err := json.NewDecoder(r.Body).Decode(&w); err != nil {
return err
}
return errors.New(w.Error)
}
type errorWrapper struct {
Error string ` + "`json:\"error\"`\n" + `
}
// Server Decode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenServerDecode}}
{{end}}
{{end}}
// Client Decode
{{range $method := .HTTPHelper.Methods}}
// DecodeHTTP{{$method.Name}} is a transport/http.DecodeResponseFunc that decodes
// a JSON-encoded {{GoName $method.ResponseType}} response from the HTTP response body.
// If the response has a non-200 status code, we will interpret that as an
// error and attempt to decode the specific error message from the response
// body. Primarily useful in a client.
func DecodeHTTP{{$method.Name}}Response(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp pb.{{GoName $method.ResponseType}}
err := json.NewDecoder(r.Body).Decode(&resp)
return &resp, err
}
{{end}}
// Client Encode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenClientEncode}}
{{end}}
{{end}}
// EncodeHTTPGenericResponse is a transport/http.EncodeResponseFunc that encodes
// the response as JSON to the response writer. Primarily useful in a server.
func EncodeHTTPGenericResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}
// Helper functions
{{.HTTPHelper.PathParamsBuilder}}
func headersToContext(ctx context.Context, r *http.Request) context.Context {
for k, _ := range r.Header {
ctx = context.WithValue(ctx, k, r.Header.Get(k))
}
return ctx
}
`
var clientTemplate = `
// Package http provides an HTTP client for the {{.Service.GetName}} service.
package http
import (
"net/url"
"strings"
"net/http"
"github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
"github.com/pkg/errors"
"golang.org/x/net/context"
// This Service
handler "{{.ImportPath -}} /handlers/server"
svc "{{.ImportPath -}} /generated"
)
var (
_ = endpoint.Chain
_ = httptransport.NewClient
)
// New returns a service backed by an HTTP server living at the remote
// instance. We expect instance to come from a service discovery system, so
// likely of the form "host:port".
func New(instance string, options ...ClientOption) (handler.Service, error) {
var cc clientConfig
for _, f := range options {
err := f(&cc)
if err != nil {
return nil, errors.Wrap(err, "cannot apply option")
}
}
clientOptions := []httptransport.ClientOption{
httptransport.ClientBefore(
contextValuesToHttpHeaders(cc.headers)),
}
if !strings.HasPrefix(instance, "http") {
instance = "http://" + instance
}
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
_ = u
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
var {{$binding.Label}}Endpoint endpoint.Endpoint
{
{{$binding.Label}}Endpoint = httptransport.NewClient(
"{{$binding.Verb}}",
copyURL(u, "{{ToLower $binding.BasePath}}"),
svc.EncodeHTTP{{$binding.Label}}Request,
svc.DecodeHTTP{{$method.Name}}Response,
clientOptions...,
).Endpoint()
}
{{- end}}
{{- end}}
return svc.Endpoints{
{{range $method := .HTTPHelper.Methods -}}
{{range $binding := $method.Bindings -}}
{{$method.Name}}Endpoint: {{$binding.Label}}Endpoint,
{{end}}
{{- end}}
}, nil
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}
type clientConfig struct {
headers []string
}
// ClientOption is a function that modifies the client config
type ClientOption func(*clientConfig) error
// CtxValuesToSend configures the http client to pull the specified keys out of
// the context and add them to the http request as headers. Note that keys
// will have net/http.CanonicalHeaderKey called on them before being send over
// the wire and that is the form they will be available in the server context.
func CtxValuesToSend(keys ...string) ClientOption {
return func(o *clientConfig) error {
o.headers = keys
return nil
}
}
func contextValuesToHttpHeaders(keys []string) httptransport.RequestFunc {
return func(ctx context.Context, r *http.Request) context.Context {
for _, k := range keys {
if v, ok := ctx.Value(k).(string); ok {
r.Header.Set(k, v)
}
}
return ctx
}
}
`

Просмотреть файл

@ -1,6 +1,7 @@
package httptransport
import (
"strings"
"testing"
"github.com/TuneLab/go-truss/gengokit/gentesthelper"
@ -97,7 +98,7 @@ func EncodeHTTPSumZeroRequest(_ context.Context, r *http.Request, request interf
}
`
if got, want := str, desired; got != want {
if got, want := strings.TrimSpace(str), strings.TrimSpace(desired); got != want {
t.Errorf("Generated code differs from result.\ngot = %s\nwant = %s", got, want)
t.Log(gentesthelper.DiffStrings(got, want))
}
@ -202,7 +203,7 @@ func DecodeHTTPSumZeroRequest(_ context.Context, r *http.Request) (interface{},
}
`
if got, want := str, desired; got != want {
if got, want := strings.TrimSpace(str), strings.TrimSpace(desired); got != want {
t.Errorf("Generated code differs from result.\ngot = %s\nwant = %s", got, want)
t.Log(gentesthelper.DiffStrings(got, want))
}

Просмотреть файл

@ -16,8 +16,8 @@ import (
// New returns an service backed by a gRPC client connection. It is the
// responsibility of the caller to dial, and later close, the connection.
func New(conn *grpc.ClientConn) handler.Service {
//options := []grpctransport.ServerOption{
//grpctransport.ServerBefore(),
//options := []grpctransport.ClientOptions{
//grpctransport.ClientBefore(),
//}
{{- with $tE := .}}

Просмотреть файл

@ -1,68 +1,2 @@
// Package http provides an HTTP client for the {{.Service.GetName}} service.
package http
import (
"net/url"
"strings"
"github.com/go-kit/kit/endpoint"
httptransport "github.com/go-kit/kit/transport/http"
// This Service
handler "{{.ImportPath -}} /handlers/server"
svc "{{.ImportPath -}} /generated"
)
var (
_ = endpoint.Chain
_ = httptransport.NewClient
)
// New returns a service backed by an HTTP server living at the remote
// instance. We expect instance to come from a service discovery system, so
// likely of the form "host:port".
func New(instance string) (handler.Service, error) {
//options := []httptransport.ServerOption{
//httptransport.ServerBefore(),
//}
if !strings.HasPrefix(instance, "http") {
instance = "http://" + instance
}
u, err := url.Parse(instance)
if err != nil {
return nil, err
}
_ = u
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
var {{$binding.Label}}Endpoint endpoint.Endpoint
{
{{$binding.Label}}Endpoint = httptransport.NewClient(
"{{$binding.Verb}}",
copyURL(u, "{{ToLower $binding.BasePath}}"),
svc.EncodeHTTP{{$binding.Label}}Request,
svc.DecodeHTTP{{$method.Name}}Response,
//options...,
).Endpoint()
}
{{- end}}
{{- end}}
return svc.Endpoints{
{{range $method := .HTTPHelper.Methods -}}
{{range $binding := $method.Bindings -}}
{{$method.Name}}Endpoint: {{$binding.Label}}Endpoint,
{{end}}
{{- end}}
}, nil
}
func copyURL(base *url.URL, path string) *url.URL {
next := *base
next.Path = path
return &next
}
{{/* See go-truss/gengokit/httptransport/template.go for code */}}
{{call .HTTPHelper.ClientTemplate .}}

Просмотреть файл

@ -1,134 +1,2 @@
package svc
// This file provides server-side bindings for the HTTP transport.
// It utilizes the transport/http.Server.
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"io"
"golang.org/x/net/context"
"github.com/go-kit/kit/log"
"github.com/pkg/errors"
httptransport "github.com/go-kit/kit/transport/http"
// This service
pb "{{.PBImportPath -}}"
)
var (
_ = fmt.Sprint
_ = bytes.Compare
_ = strconv.Atoi
_ = httptransport.NewServer
_ = ioutil.NopCloser
_ = pb.Register{{.Service.GetName}}Server
_ = io.Copy
)
// MakeHTTPHandler returns a handler that makes a set of endpoints available
// on predefined paths.
func MakeHTTPHandler(ctx context.Context, endpoints Endpoints, logger log.Logger) http.Handler {
/*options := []httptransport.ServerOption{
httptransport.ServerErrorEncoder(errorEncoder),
httptransport.ServerErrorLogger(logger),
}*/
m := http.NewServeMux()
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
m.Handle("{{ToLower $binding.BasePath}}", httptransport.NewServer(
ctx,
endpoints.{{$method.Name}}Endpoint,
HttpDecodeLogger(DecodeHTTP{{$binding.Label}}Request, logger),
EncodeHTTPGenericResponse,
//options...,
))
{{- end}}
{{- end}}
return m
}
func HttpDecodeLogger(next httptransport.DecodeRequestFunc, logger log.Logger) httptransport.DecodeRequestFunc {
return func(ctx context.Context, r *http.Request) (interface{}, error) {
logger.Log("method", r.Method, "url", r.URL.String())
rv, err := next(ctx, r)
if err != nil {
logger.Log("method", r.Method, "url", r.URL.String(), "Error", err)
}
return rv, err
}
}
func errorEncoder(_ context.Context, err error, w http.ResponseWriter) {
code := http.StatusInternalServerError
msg := err.Error()
w.WriteHeader(code)
json.NewEncoder(w).Encode(errorWrapper{Error: msg})
}
func errorDecoder(r *http.Response) error {
var w errorWrapper
if err := json.NewDecoder(r.Body).Decode(&w); err != nil {
return err
}
return errors.New(w.Error)
}
type errorWrapper struct {
Error string `json:"error"`
}
// Server Decode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenServerDecode}}
{{end}}
{{end}}
// Client Decode
{{range $method := .HTTPHelper.Methods}}
// DecodeHTTP{{$method.Name}} is a transport/http.DecodeResponseFunc that decodes
// a JSON-encoded {{GoName $method.ResponseType}} response from the HTTP response body.
// If the response has a non-200 status code, we will interpret that as an
// error and attempt to decode the specific error message from the response
// body. Primarily useful in a client.
func DecodeHTTP{{$method.Name}}Response(_ context.Context, r *http.Response) (interface{}, error) {
if r.StatusCode != http.StatusOK {
return nil, errorDecoder(r)
}
var resp pb.{{GoName $method.ResponseType}}
err := json.NewDecoder(r.Body).Decode(&resp)
return &resp, err
}
{{end}}
// Client Encode
{{range $method := .HTTPHelper.Methods}}
{{range $binding := $method.Bindings}}
{{$binding.GenClientEncode}}
{{end}}
{{end}}
// EncodeHTTPGenericResponse is a transport/http.EncodeResponseFunc that encodes
// the response as JSON to the response writer. Primarily useful in a server.
func EncodeHTTPGenericResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {
return json.NewEncoder(w).Encode(response)
}
// Helper functions
{{.HTTPHelper.PathParamsBuilder}}
{{/* See go-truss/gengokit/httptransport/template.go for code */}}
{{call .HTTPHelper.ServerTemplate .}}

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -24,7 +24,6 @@ type httptestService struct{}
// GetWithQuery implements Service.
func (s httptestService) GetWithQuery(ctx context.Context, in *pb.GetWithQueryRequest) (*pb.GetWithQueryResponse, error) {
response := pb.GetWithQueryResponse{
V: in.A + in.B,
}
@ -49,16 +48,31 @@ func (s httptestService) GetWithRepeatedQuery(ctx context.Context, in *pb.GetWit
// PostWithNestedMessageBody implements Service.
func (s httptestService) PostWithNestedMessageBody(ctx context.Context, in *pb.PostWithNestedMessageBodyRequest) (*pb.PostWithNestedMessageBodyResponse, error) {
_ = ctx
_ = in
response := pb.PostWithNestedMessageBodyResponse{
V: in.NM.A + in.NM.B,
}
return &response, nil
}
// CtxtToCtxtViaHTTPHeader implements Service.
func (s httptestService) CtxToCtxViaHTTPHeader(ctx context.Context, in *pb.HeaderRequest) (*pb.HeaderResponse, error) {
var resp pb.HeaderResponse
val := ctx.Value(in.HeaderKey)
if v, ok := val.(string); ok {
resp.V = v
} else if val == nil {
resp.V = "CONTEXT VALUE FOR KEY IS NILL"
} else {
resp.V = "CONTEXT VALUE FOR KEY IS NON STRING"
}
return &resp, nil
}
type Service interface {
GetWithQuery(ctx context.Context, in *pb.GetWithQueryRequest) (*pb.GetWithQueryResponse, error)
GetWithRepeatedQuery(ctx context.Context, in *pb.GetWithRepeatedQueryRequest) (*pb.GetWithRepeatedQueryResponse, error)
PostWithNestedMessageBody(ctx context.Context, in *pb.PostWithNestedMessageBodyRequest) (*pb.PostWithNestedMessageBodyResponse, error)
CtxToCtxViaHTTPHeader(ctx context.Context, in *pb.HeaderRequest) (*pb.HeaderResponse, error)
}

Просмотреть файл

@ -10,6 +10,7 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
// 3d Party
"golang.org/x/net/context"
@ -43,11 +44,13 @@ func init() {
getWithQueryE := svc.MakeGetWithQueryEndpoint(service)
getWithRepeatedQueryE := svc.MakeGetWithRepeatedQueryEndpoint(service)
postWithNestedMessageBodyE := svc.MakePostWithNestedMessageBodyEndpoint(service)
ctxToCtxViaHTTPHeaderE := svc.MakeCtxToCtxViaHTTPHeaderEndpoint(service)
endpoints := svc.Endpoints{
GetWithQueryEndpoint: getWithQueryE,
GetWithRepeatedQueryEndpoint: getWithRepeatedQueryE,
PostWithNestedMessageBodyEndpoint: postWithNestedMessageBodyE,
CtxToCtxViaHTTPHeaderEndpoint: ctxToCtxViaHTTPHeaderE,
}
ctx := context.Background()
@ -91,6 +94,9 @@ func TestGetWithQueryRequest(t *testing.T) {
route: fmt.Sprintf(routeFormat, routeFields...),
body: bodyBytes,
}.Test(t)
if err != nil {
t.Fatal(errors.Wrap(err, "cannot make http request"))
}
err = json.Unmarshal(respBytes, &resp)
if err != nil {
@ -138,6 +144,9 @@ func TestGetWithRepeatedQueryRequest(t *testing.T) {
route: fmt.Sprintf(routeFormat, routeFields...),
body: bodyBytes,
}.Test(t)
if err != nil {
t.Fatal(errors.Wrap(err, "cannot make http request"))
}
err = json.Unmarshal(respBytes, &resp)
if err != nil {
@ -194,6 +203,9 @@ func TestPostWithNestedMessageBodyRequest(t *testing.T) {
route: fmt.Sprintf(routeFormat, routeFields...),
body: bodyBytes,
}.Test(t)
if err != nil {
t.Fatal(errors.Wrap(err, "cannot make http request"))
}
err = json.Unmarshal(respBytes, &resp)
if err != nil {
@ -210,6 +222,64 @@ func TestPostWithNestedMessageBodyRequest(t *testing.T) {
testHTTP([]byte(jsonStr), "POST", "postwithnestedmessagebody")
}
func TestCtxToCtxViaHTTPHeaderClient(t *testing.T) {
var req pb.HeaderRequest
var key, value = "Truss-Auth-Header", "SECRET"
req.HeaderKey = key
// Create a new client telling it to send "Truss-Auth-Header" as a header
svchttp, err := httpclient.New(httpserver.URL,
httpclient.CtxValuesToSend(key))
if err != nil {
t.Fatalf("failed to create httpclient: %q", err)
}
// Create a context with the header key and value
ctx := context.WithValue(context.Background(), key, value)
// send the context
resp, err := svchttp.CtxToCtxViaHTTPHeader(ctx, &req)
if err != nil {
t.Fatalf("httpclient returned error: %q", err)
}
if resp.V != value {
t.Fatalf("Expect: %q, got %q", value, resp.V)
}
}
func TestCtxToCtxViaHTTPHeaderRequest(t *testing.T) {
var resp pb.HeaderResponse
var key, value = "Truss-Auth-Header", "SECRET"
jsonStr := fmt.Sprintf(`{ "HeaderKey": %q }`, key)
fmt.Println(jsonStr)
req, err := http.NewRequest("POST", httpserver.URL+"/"+"ctxtoctx", strings.NewReader(jsonStr))
if err != nil {
t.Fatal(errors.Wrap(err, "cannot construct http request"))
}
req.Header.Set(key, value)
respBytes, err := testHTTPRequest(req)
if err != nil {
t.Fatal(errors.Wrap(err, "cannot make http request"))
}
err = json.Unmarshal(respBytes, &resp)
if err != nil {
t.Fatal(errors.Wrapf(err, "json error, got json: %q", string(respBytes)))
}
if resp.V != value {
t.Fatalf("Expect: %q, got %q", value, resp.V)
}
}
// Helpers
type httpRequestBuilder struct {
method string
route string

Просмотреть файл

@ -21,6 +21,12 @@ service HTTPPermutations {
body: "*"
};
}
rpc CtxToCtxViaHTTPHeader (HeaderRequest) returns (HeaderResponse){
option (google.api.http) = {
post: "/ctxtoctx"
body: "*"
};
}
}
message GetWithQueryRequest {
@ -53,3 +59,10 @@ message PostWithNestedMessageBodyResponse {
int64 V = 1;
}
message HeaderRequest{
string HeaderKey = 1;
}
message HeaderResponse {
string V = 1;
}