| @@ -1,121 +0,0 @@ | ||||
| // Package api provides an http-rpc handler which provides the entire http request over rpc | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	goapi "github.com/unistack-org/micro/v3/api" | ||||
| 	"github.com/unistack-org/micro/v3/api/handler" | ||||
| 	api "github.com/unistack-org/micro/v3/api/proto" | ||||
| 	"github.com/unistack-org/micro/v3/client" | ||||
| 	"github.com/unistack-org/micro/v3/errors" | ||||
| 	"github.com/unistack-org/micro/v3/util/ctx" | ||||
| 	"github.com/unistack-org/micro/v3/util/router" | ||||
| ) | ||||
|  | ||||
| type apiHandler struct { | ||||
| 	opts handler.Options | ||||
| 	s    *goapi.Service | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	Handler = "api" | ||||
| ) | ||||
|  | ||||
| // API handler is the default handler which takes api.Request and returns api.Response | ||||
| func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	bsize := handler.DefaultMaxRecvSize | ||||
| 	if a.opts.MaxRecvSize > 0 { | ||||
| 		bsize = a.opts.MaxRecvSize | ||||
| 	} | ||||
|  | ||||
| 	r.Body = http.MaxBytesReader(w, r.Body, bsize) | ||||
| 	request, err := requestToProto(r) | ||||
| 	if err != nil { | ||||
| 		er := errors.InternalServerError("go.micro.api", err.Error()) | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		w.WriteHeader(500) | ||||
| 		w.Write([]byte(er.Error())) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var service *goapi.Service | ||||
|  | ||||
| 	if a.s != nil { | ||||
| 		// we were given the service | ||||
| 		service = a.s | ||||
| 	} else if a.opts.Router != nil { | ||||
| 		// try get service from router | ||||
| 		s, err := a.opts.Router.Route(r) | ||||
| 		if err != nil { | ||||
| 			er := errors.InternalServerError("go.micro.api", err.Error()) | ||||
| 			w.Header().Set("Content-Type", "application/json") | ||||
| 			w.WriteHeader(500) | ||||
| 			w.Write([]byte(er.Error())) | ||||
| 			return | ||||
| 		} | ||||
| 		service = s | ||||
| 	} else { | ||||
| 		// we have no way of routing the request | ||||
| 		er := errors.InternalServerError("go.micro.api", "no route found") | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		w.WriteHeader(500) | ||||
| 		w.Write([]byte(er.Error())) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// create request and response | ||||
| 	c := a.opts.Client | ||||
| 	req := c.NewRequest(service.Name, service.Endpoint.Name, request) | ||||
| 	rsp := &api.Response{} | ||||
|  | ||||
| 	// create the context from headers | ||||
| 	cx := ctx.FromRequest(r) | ||||
|  | ||||
| 	if err := c.Call(cx, req, rsp, client.WithRouter(router.New(service.Services))); err != nil { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		ce := errors.Parse(err.Error()) | ||||
| 		switch ce.Code { | ||||
| 		case 0: | ||||
| 			w.WriteHeader(500) | ||||
| 		default: | ||||
| 			w.WriteHeader(int(ce.Code)) | ||||
| 		} | ||||
| 		w.Write([]byte(ce.Error())) | ||||
| 		return | ||||
| 	} else if rsp.StatusCode == 0 { | ||||
| 		rsp.StatusCode = http.StatusOK | ||||
| 	} | ||||
|  | ||||
| 	for _, header := range rsp.GetHeader() { | ||||
| 		for _, val := range header.Values { | ||||
| 			w.Header().Add(header.Key, val) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(w.Header().Get("Content-Type")) == 0 { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 	} | ||||
|  | ||||
| 	w.WriteHeader(int(rsp.StatusCode)) | ||||
| 	w.Write([]byte(rsp.Body)) | ||||
| } | ||||
|  | ||||
| func (a *apiHandler) String() string { | ||||
| 	return "api" | ||||
| } | ||||
|  | ||||
| func NewHandler(opts ...handler.Option) handler.Handler { | ||||
| 	options := handler.NewOptions(opts...) | ||||
| 	return &apiHandler{ | ||||
| 		opts: options, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithService(s *goapi.Service, opts ...handler.Option) handler.Handler { | ||||
| 	options := handler.NewOptions(opts...) | ||||
| 	return &apiHandler{ | ||||
| 		opts: options, | ||||
| 		s:    s, | ||||
| 	} | ||||
| } | ||||
| @@ -1,109 +0,0 @@ | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"mime" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/oxtoacart/bpool" | ||||
| 	api "github.com/unistack-org/micro/v3/api/proto" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// need to calculate later to specify useful defaults | ||||
| 	bufferPool = bpool.NewSizedBufferPool(1024, 8) | ||||
| ) | ||||
|  | ||||
| func requestToProto(r *http.Request) (*api.Request, error) { | ||||
| 	if err := r.ParseForm(); err != nil { | ||||
| 		return nil, fmt.Errorf("Error parsing form: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	req := &api.Request{ | ||||
| 		Path:   r.URL.Path, | ||||
| 		Method: r.Method, | ||||
| 		Header: make(map[string]*api.Pair), | ||||
| 		Get:    make(map[string]*api.Pair), | ||||
| 		Post:   make(map[string]*api.Pair), | ||||
| 		Url:    r.URL.String(), | ||||
| 	} | ||||
|  | ||||
| 	ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) | ||||
| 	if err != nil { | ||||
| 		ct = "text/plain; charset=UTF-8" //default CT is text/plain | ||||
| 		r.Header.Set("Content-Type", ct) | ||||
| 	} | ||||
|  | ||||
| 	//set the body: | ||||
| 	if r.Body != nil { | ||||
| 		switch ct { | ||||
| 		case "application/x-www-form-urlencoded": | ||||
| 			// expect form vals in Post data | ||||
| 		default: | ||||
| 			buf := bufferPool.Get() | ||||
| 			defer bufferPool.Put(buf) | ||||
| 			if _, err = buf.ReadFrom(r.Body); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			req.Body = buf.String() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Set X-Forwarded-For if it does not exist | ||||
| 	if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { | ||||
| 		if prior, ok := r.Header["X-Forwarded-For"]; ok { | ||||
| 			ip = strings.Join(prior, ", ") + ", " + ip | ||||
| 		} | ||||
|  | ||||
| 		// Set the header | ||||
| 		req.Header["X-Forwarded-For"] = &api.Pair{ | ||||
| 			Key:    "X-Forwarded-For", | ||||
| 			Values: []string{ip}, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Host is stripped from net/http Headers so let's add it | ||||
| 	req.Header["Host"] = &api.Pair{ | ||||
| 		Key:    "Host", | ||||
| 		Values: []string{r.Host}, | ||||
| 	} | ||||
|  | ||||
| 	// Get data | ||||
| 	for key, vals := range r.URL.Query() { | ||||
| 		header, ok := req.Get[key] | ||||
| 		if !ok { | ||||
| 			header = &api.Pair{ | ||||
| 				Key: key, | ||||
| 			} | ||||
| 			req.Get[key] = header | ||||
| 		} | ||||
| 		header.Values = vals | ||||
| 	} | ||||
|  | ||||
| 	// Post data | ||||
| 	for key, vals := range r.PostForm { | ||||
| 		header, ok := req.Post[key] | ||||
| 		if !ok { | ||||
| 			header = &api.Pair{ | ||||
| 				Key: key, | ||||
| 			} | ||||
| 			req.Post[key] = header | ||||
| 		} | ||||
| 		header.Values = vals | ||||
| 	} | ||||
|  | ||||
| 	for key, vals := range r.Header { | ||||
| 		header, ok := req.Header[key] | ||||
| 		if !ok { | ||||
| 			header = &api.Pair{ | ||||
| 				Key: key, | ||||
| 			} | ||||
| 			req.Header[key] = header | ||||
| 		} | ||||
| 		header.Values = vals | ||||
| 	} | ||||
|  | ||||
| 	return req, nil | ||||
| } | ||||
| @@ -1,44 +0,0 @@ | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestRequestToProto(t *testing.T) { | ||||
| 	testData := []*http.Request{ | ||||
| 		{ | ||||
| 			Method: "GET", | ||||
| 			Header: http.Header{ | ||||
| 				"Header": []string{"test"}, | ||||
| 			}, | ||||
| 			URL: &url.URL{ | ||||
| 				Scheme:   "http", | ||||
| 				Host:     "localhost", | ||||
| 				Path:     "/foo/bar", | ||||
| 				RawQuery: "param1=value1", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, d := range testData { | ||||
| 		p, err := requestToProto(d) | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
| 		if p.Path != d.URL.Path { | ||||
| 			t.Fatalf("Expected path %s got %s", d.URL.Path, p.Path) | ||||
| 		} | ||||
| 		if p.Method != d.Method { | ||||
| 			t.Fatalf("Expected method %s got %s", d.Method, p.Method) | ||||
| 		} | ||||
| 		for k, v := range d.Header { | ||||
| 			if val, ok := p.Header[k]; !ok { | ||||
| 				t.Fatalf("Expected header %s", k) | ||||
| 			} else if val.Values[0] != v[0] { | ||||
| 				t.Fatalf("Expected val %s, got %s", val.Values[0], v[0]) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,141 +0,0 @@ | ||||
| // Package event provides a handler which publishes an event | ||||
| package event | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/oxtoacart/bpool" | ||||
| 	"github.com/unistack-org/micro/v3/api/handler" | ||||
| 	proto "github.com/unistack-org/micro/v3/api/proto" | ||||
| 	"github.com/unistack-org/micro/v3/util/ctx" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	bufferPool = bpool.NewSizedBufferPool(1024, 8) | ||||
| ) | ||||
|  | ||||
| type event struct { | ||||
| 	opts handler.Options | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	Handler   = "event" | ||||
| 	versionRe = regexp.MustCompilePOSIX("^v[0-9]+$") | ||||
| ) | ||||
|  | ||||
| func eventName(parts []string) string { | ||||
| 	return strings.Join(parts, ".") | ||||
| } | ||||
|  | ||||
| func evRoute(ns, p string) (string, string) { | ||||
| 	p = path.Clean(p) | ||||
| 	p = strings.TrimPrefix(p, "/") | ||||
|  | ||||
| 	if len(p) == 0 { | ||||
| 		return ns, "event" | ||||
| 	} | ||||
|  | ||||
| 	parts := strings.Split(p, "/") | ||||
|  | ||||
| 	// no path | ||||
| 	if len(parts) == 0 { | ||||
| 		// topic: namespace | ||||
| 		// action: event | ||||
| 		return strings.Trim(ns, "."), "event" | ||||
| 	} | ||||
|  | ||||
| 	// Treat /v[0-9]+ as versioning | ||||
| 	// /v1/foo/bar => topic: v1.foo action: bar | ||||
| 	if len(parts) >= 2 && versionRe.Match([]byte(parts[0])) { | ||||
| 		topic := ns + "." + strings.Join(parts[:2], ".") | ||||
| 		action := eventName(parts[1:]) | ||||
| 		return topic, action | ||||
| 	} | ||||
|  | ||||
| 	// /foo => topic: ns.foo action: foo | ||||
| 	// /foo/bar => topic: ns.foo action: bar | ||||
| 	topic := ns + "." + strings.Join(parts[:1], ".") | ||||
| 	action := eventName(parts[1:]) | ||||
|  | ||||
| 	return topic, action | ||||
| } | ||||
|  | ||||
| func (e *event) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	bsize := handler.DefaultMaxRecvSize | ||||
| 	if e.opts.MaxRecvSize > 0 { | ||||
| 		bsize = e.opts.MaxRecvSize | ||||
| 	} | ||||
|  | ||||
| 	r.Body = http.MaxBytesReader(w, r.Body, bsize) | ||||
|  | ||||
| 	// request to topic:event | ||||
| 	// create event | ||||
| 	// publish to topic | ||||
|  | ||||
| 	topic, action := evRoute(e.opts.Namespace, r.URL.Path) | ||||
|  | ||||
| 	// create event | ||||
| 	ev := &proto.Event{ | ||||
| 		Name: action, | ||||
| 		// TODO: dedupe event | ||||
| 		Id:        fmt.Sprintf("%s-%s-%s", topic, action, uuid.New().String()), | ||||
| 		Header:    make(map[string]*proto.Pair), | ||||
| 		Timestamp: time.Now().Unix(), | ||||
| 	} | ||||
|  | ||||
| 	// set headers | ||||
| 	for key, vals := range r.Header { | ||||
| 		header, ok := ev.Header[key] | ||||
| 		if !ok { | ||||
| 			header = &proto.Pair{ | ||||
| 				Key: key, | ||||
| 			} | ||||
| 			ev.Header[key] = header | ||||
| 		} | ||||
| 		header.Values = vals | ||||
| 	} | ||||
|  | ||||
| 	// set body | ||||
| 	if r.Method == "GET" { | ||||
| 		bytes, _ := json.Marshal(r.URL.Query()) | ||||
| 		ev.Data = string(bytes) | ||||
| 	} else { | ||||
| 		// Read body | ||||
| 		buf := bufferPool.Get() | ||||
| 		defer bufferPool.Put(buf) | ||||
| 		if _, err := buf.ReadFrom(r.Body); err != nil { | ||||
| 			http.Error(w, err.Error(), 500) | ||||
| 			return | ||||
| 		} | ||||
| 		ev.Data = buf.String() | ||||
| 	} | ||||
|  | ||||
| 	// get client | ||||
| 	c := e.opts.Client | ||||
|  | ||||
| 	// create publication | ||||
| 	p := c.NewMessage(topic, ev) | ||||
|  | ||||
| 	// publish event | ||||
| 	if err := c.Publish(ctx.FromRequest(r), p); err != nil { | ||||
| 		http.Error(w, err.Error(), 500) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *event) String() string { | ||||
| 	return "event" | ||||
| } | ||||
|  | ||||
| func NewHandler(opts ...handler.Option) handler.Handler { | ||||
| 	return &event{ | ||||
| 		opts: handler.NewOptions(opts...), | ||||
| 	} | ||||
| } | ||||
| @@ -1,105 +0,0 @@ | ||||
| // Package http is a http reverse proxy handler | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"math/rand" | ||||
| 	"net/http" | ||||
| 	"net/http/httputil" | ||||
| 	"net/url" | ||||
|  | ||||
| 	"github.com/unistack-org/micro/v3/api" | ||||
| 	"github.com/unistack-org/micro/v3/api/handler" | ||||
| 	"github.com/unistack-org/micro/v3/register" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	Handler = "http" | ||||
| ) | ||||
|  | ||||
| type httpHandler struct { | ||||
| 	options handler.Options | ||||
|  | ||||
| 	// set with different initializer | ||||
| 	s *api.Service | ||||
| } | ||||
|  | ||||
| func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	service, err := h.getService(r) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(500) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if len(service) == 0 { | ||||
| 		w.WriteHeader(404) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	rp, err := url.Parse(service) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(500) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	httputil.NewSingleHostReverseProxy(rp).ServeHTTP(w, r) | ||||
| } | ||||
|  | ||||
| // getService returns the service for this request from the selector | ||||
| func (h *httpHandler) getService(r *http.Request) (string, error) { | ||||
| 	var service *api.Service | ||||
|  | ||||
| 	if h.s != nil { | ||||
| 		// we were given the service | ||||
| 		service = h.s | ||||
| 	} else if h.options.Router != nil { | ||||
| 		// try get service from router | ||||
| 		s, err := h.options.Router.Route(r) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		service = s | ||||
| 	} else { | ||||
| 		// we have no way of routing the request | ||||
| 		return "", errors.New("no route found") | ||||
| 	} | ||||
|  | ||||
| 	if len(service.Services) == 0 { | ||||
| 		return "", errors.New("no route found") | ||||
| 	} | ||||
|  | ||||
| 	// get the nodes for this service | ||||
| 	nodes := make([]*register.Node, 0, len(service.Services)) | ||||
| 	for _, srv := range service.Services { | ||||
| 		nodes = append(nodes, srv.Nodes...) | ||||
| 	} | ||||
|  | ||||
| 	// select a random node | ||||
| 	node := nodes[rand.Int()%len(nodes)] | ||||
|  | ||||
| 	return fmt.Sprintf("http://%s", node.Address), nil | ||||
| } | ||||
|  | ||||
| func (h *httpHandler) String() string { | ||||
| 	return "http" | ||||
| } | ||||
|  | ||||
| // NewHandler returns a http proxy handler | ||||
| func NewHandler(opts ...handler.Option) handler.Handler { | ||||
| 	options := handler.NewOptions(opts...) | ||||
|  | ||||
| 	return &httpHandler{ | ||||
| 		options: options, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithService creates a handler with a service | ||||
| func WithService(s *api.Service, opts ...handler.Option) handler.Handler { | ||||
| 	options := handler.NewOptions(opts...) | ||||
|  | ||||
| 	return &httpHandler{ | ||||
| 		options: options, | ||||
| 		s:       s, | ||||
| 	} | ||||
| } | ||||
| @@ -1,129 +0,0 @@ | ||||
| // +build ignore | ||||
|  | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/unistack-org/micro/v3/api/handler" | ||||
| 	"github.com/unistack-org/micro/v3/api/resolver" | ||||
| 	"github.com/unistack-org/micro/v3/api/resolver/vpath" | ||||
| 	"github.com/unistack-org/micro/v3/api/router" | ||||
| 	regRouter "github.com/unistack-org/micro/v3/api/router/register" | ||||
| 	"github.com/unistack-org/micro/v3/register" | ||||
| 	"github.com/unistack-org/micro/v3/register/memory" | ||||
| ) | ||||
|  | ||||
| func testHttp(t *testing.T, path, service, ns string) { | ||||
| 	r := memory.NewRegister() | ||||
|  | ||||
| 	l, err := net.Listen("tcp", "127.0.0.1:0") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	defer l.Close() | ||||
|  | ||||
| 	s := ®ister.Service{ | ||||
| 		Name: service, | ||||
| 		Nodes: []*register.Node{ | ||||
| 			{ | ||||
| 				Id:      service + "-1", | ||||
| 				Address: l.Addr().String(), | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	r.Register(s) | ||||
| 	defer r.Deregister(s) | ||||
|  | ||||
| 	// setup the test handler | ||||
| 	m := http.NewServeMux() | ||||
| 	m.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Write([]byte(`you got served`)) | ||||
| 	}) | ||||
|  | ||||
| 	// start http test serve | ||||
| 	go http.Serve(l, m) | ||||
|  | ||||
| 	// create new request and writer | ||||
| 	w := httptest.NewRecorder() | ||||
| 	req, err := http.NewRequest("POST", path, nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// initialise the handler | ||||
| 	rt := regRouter.NewRouter( | ||||
| 		router.WithHandler("http"), | ||||
| 		router.WithRegister(r), | ||||
| 		router.WithResolver(vpath.NewResolver( | ||||
| 			resolver.WithServicePrefix(ns), | ||||
| 		)), | ||||
| 	) | ||||
|  | ||||
| 	p := NewHandler(handler.WithRouter(rt)) | ||||
|  | ||||
| 	// execute the handler | ||||
| 	p.ServeHTTP(w, req) | ||||
|  | ||||
| 	if w.Code != 200 { | ||||
| 		t.Fatalf("Expected 200 response got %d %s", w.Code, w.Body.String()) | ||||
| 	} | ||||
|  | ||||
| 	if w.Body.String() != "you got served" { | ||||
| 		t.Fatalf("Expected body: you got served. Got: %s", w.Body.String()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestHttpHandler(t *testing.T) { | ||||
| 	testData := []struct { | ||||
| 		path      string | ||||
| 		service   string | ||||
| 		namespace string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			"/test/foo", | ||||
| 			"go.micro.api.test", | ||||
| 			"go.micro.api", | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/test/foo/baz", | ||||
| 			"go.micro.api.test", | ||||
| 			"go.micro.api", | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/v1/foo", | ||||
| 			"go.micro.api.v1.foo", | ||||
| 			"go.micro.api", | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/v1/foo/bar", | ||||
| 			"go.micro.api.v1.foo", | ||||
| 			"go.micro.api", | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/v2/baz", | ||||
| 			"go.micro.api.v2.baz", | ||||
| 			"go.micro.api", | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/v2/baz/bar", | ||||
| 			"go.micro.api.v2.baz", | ||||
| 			"go.micro.api", | ||||
| 		}, | ||||
| 		{ | ||||
| 			"/v2/baz/bar", | ||||
| 			"v2.baz", | ||||
| 			"", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, d := range testData { | ||||
| 		t.Run(d.service, func(t *testing.T) { | ||||
| 			testHttp(t, d.path, d.service, d.namespace) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| @@ -3,6 +3,7 @@ package handler | ||||
| import ( | ||||
| 	"github.com/unistack-org/micro/v3/api/router" | ||||
| 	"github.com/unistack-org/micro/v3/client" | ||||
| 	"github.com/unistack-org/micro/v3/logger" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @@ -14,13 +15,19 @@ type Options struct { | ||||
| 	Namespace   string | ||||
| 	Router      router.Router | ||||
| 	Client      client.Client | ||||
| 	Logger      logger.Logger | ||||
| } | ||||
|  | ||||
| type Option func(o *Options) | ||||
|  | ||||
| // NewOptions fills in the blanks | ||||
| func NewOptions(opts ...Option) Options { | ||||
| 	var options Options | ||||
| 	options := Options{ | ||||
| 		Client:      client.DefaultClient, | ||||
| 		Router:      router.DefaultRouter, | ||||
| 		Logger:      logger.DefaultLogger, | ||||
| 		MaxRecvSize: DefaultMaxRecvSize, | ||||
| 	} | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
| @@ -30,10 +37,6 @@ func NewOptions(opts ...Option) Options { | ||||
| 		WithNamespace("go.micro.api")(&options) | ||||
| 	} | ||||
|  | ||||
| 	if options.MaxRecvSize == 0 { | ||||
| 		options.MaxRecvSize = DefaultMaxRecvSize | ||||
| 	} | ||||
|  | ||||
| 	return options | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,182 +0,0 @@ | ||||
| // Package web contains the web handler including websocket support | ||||
| package web | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/rand" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"net/http/httputil" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/unistack-org/micro/v3/api" | ||||
| 	"github.com/unistack-org/micro/v3/api/handler" | ||||
| 	"github.com/unistack-org/micro/v3/register" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	Handler = "web" | ||||
| ) | ||||
|  | ||||
| type webHandler struct { | ||||
| 	opts handler.Options | ||||
| 	s    *api.Service | ||||
| } | ||||
|  | ||||
| func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||||
| 	service, err := wh.getService(r) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(500) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if len(service) == 0 { | ||||
| 		w.WriteHeader(404) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	rp, err := url.Parse(service) | ||||
| 	if err != nil { | ||||
| 		w.WriteHeader(500) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if isWebSocket(r) { | ||||
| 		wh.serveWebSocket(rp.Host, w, r) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	httputil.NewSingleHostReverseProxy(rp).ServeHTTP(w, r) | ||||
| } | ||||
|  | ||||
| // getService returns the service for this request from the selector | ||||
| func (wh *webHandler) getService(r *http.Request) (string, error) { | ||||
| 	var service *api.Service | ||||
|  | ||||
| 	if wh.s != nil { | ||||
| 		// we were given the service | ||||
| 		service = wh.s | ||||
| 	} else if wh.opts.Router != nil { | ||||
| 		// try get service from router | ||||
| 		s, err := wh.opts.Router.Route(r) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		service = s | ||||
| 	} else { | ||||
| 		// we have no way of routing the request | ||||
| 		return "", errors.New("no route found") | ||||
| 	} | ||||
|  | ||||
| 	// get the nodes | ||||
| 	nodes := make([]*register.Node, 0, len(service.Services)) | ||||
| 	for _, srv := range service.Services { | ||||
| 		nodes = append(nodes, srv.Nodes...) | ||||
| 	} | ||||
|  | ||||
| 	if len(nodes) == 0 { | ||||
| 		return "", errors.New("no route found") | ||||
| 	} | ||||
|  | ||||
| 	// select a random node | ||||
| 	node := nodes[rand.Int()%len(nodes)] | ||||
|  | ||||
| 	return fmt.Sprintf("http://%s", node.Address), nil | ||||
| } | ||||
|  | ||||
| // serveWebSocket used to serve a web socket proxied connection | ||||
| func (wh *webHandler) serveWebSocket(host string, w http.ResponseWriter, r *http.Request) { | ||||
| 	req := new(http.Request) | ||||
| 	*req = *r | ||||
|  | ||||
| 	if len(host) == 0 { | ||||
| 		http.Error(w, "invalid host", 500) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// set x-forward-for | ||||
| 	if clientIP, _, err := net.SplitHostPort(r.RemoteAddr); err == nil { | ||||
| 		if ips, ok := req.Header["X-Forwarded-For"]; ok { | ||||
| 			clientIP = strings.Join(ips, ", ") + ", " + clientIP | ||||
| 		} | ||||
| 		req.Header.Set("X-Forwarded-For", clientIP) | ||||
| 	} | ||||
|  | ||||
| 	// connect to the backend host | ||||
| 	conn, err := net.Dial("tcp", host) | ||||
| 	if err != nil { | ||||
| 		http.Error(w, err.Error(), 500) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// hijack the connection | ||||
| 	hj, ok := w.(http.Hijacker) | ||||
| 	if !ok { | ||||
| 		http.Error(w, "failed to connect", 500) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	nc, _, err := hj.Hijack() | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	defer nc.Close() | ||||
| 	defer conn.Close() | ||||
|  | ||||
| 	if err = req.Write(conn); err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	errCh := make(chan error, 2) | ||||
|  | ||||
| 	cp := func(dst io.Writer, src io.Reader) { | ||||
| 		_, err := io.Copy(dst, src) | ||||
| 		errCh <- err | ||||
| 	} | ||||
|  | ||||
| 	go cp(conn, nc) | ||||
| 	go cp(nc, conn) | ||||
|  | ||||
| 	<-errCh | ||||
| } | ||||
|  | ||||
| func isWebSocket(r *http.Request) bool { | ||||
| 	contains := func(key, val string) bool { | ||||
| 		vv := strings.Split(r.Header.Get(key), ",") | ||||
| 		for _, v := range vv { | ||||
| 			if val == strings.ToLower(strings.TrimSpace(v)) { | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	if contains("Connection", "upgrade") && contains("Upgrade", "websocket") { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (wh *webHandler) String() string { | ||||
| 	return "web" | ||||
| } | ||||
|  | ||||
| func NewHandler(opts ...handler.Option) handler.Handler { | ||||
| 	return &webHandler{ | ||||
| 		opts: handler.NewOptions(opts...), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithService(s *api.Service, opts ...handler.Option) handler.Handler { | ||||
| 	options := handler.NewOptions(opts...) | ||||
|  | ||||
| 	return &webHandler{ | ||||
| 		opts: options, | ||||
| 		s:    s, | ||||
| 	} | ||||
| } | ||||
| @@ -1,28 +0,0 @@ | ||||
| package proto | ||||
|  | ||||
| type Message struct { | ||||
| 	data []byte | ||||
| } | ||||
|  | ||||
| func (m *Message) ProtoMessage() {} | ||||
|  | ||||
| func (m *Message) Reset() { | ||||
| 	*m = Message{} | ||||
| } | ||||
|  | ||||
| func (m *Message) String() string { | ||||
| 	return string(m.data) | ||||
| } | ||||
|  | ||||
| func (m *Message) Marshal() ([]byte, error) { | ||||
| 	return m.data, nil | ||||
| } | ||||
|  | ||||
| func (m *Message) Unmarshal(data []byte) error { | ||||
| 	m.data = data | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func NewMessage(data []byte) *Message { | ||||
| 	return &Message{data} | ||||
| } | ||||
| @@ -1,511 +0,0 @@ | ||||
| // Code generated by protoc-gen-go. DO NOT EDIT. | ||||
| // versions: | ||||
| // 	protoc-gen-go v1.25.0 | ||||
| // 	protoc        v3.6.1 | ||||
| // source: api/proto/api.proto | ||||
|  | ||||
| package go_api | ||||
|  | ||||
| import ( | ||||
| 	proto "github.com/golang/protobuf/proto" | ||||
| 	protoreflect "google.golang.org/protobuf/reflect/protoreflect" | ||||
| 	protoimpl "google.golang.org/protobuf/runtime/protoimpl" | ||||
| 	reflect "reflect" | ||||
| 	sync "sync" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// Verify that this generated code is sufficiently up-to-date. | ||||
| 	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) | ||||
| 	// Verify that runtime/protoimpl is sufficiently up-to-date. | ||||
| 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) | ||||
| ) | ||||
|  | ||||
| // This is a compile-time assertion that a sufficiently up-to-date version | ||||
| // of the legacy proto package is being used. | ||||
| const _ = proto.ProtoPackageIsVersion4 | ||||
|  | ||||
| type Pair struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
|  | ||||
| 	Key    string   `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` | ||||
| 	Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` | ||||
| } | ||||
|  | ||||
| func (x *Pair) Reset() { | ||||
| 	*x = Pair{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_api_proto_api_proto_msgTypes[0] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (x *Pair) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
|  | ||||
| func (*Pair) ProtoMessage() {} | ||||
|  | ||||
| func (x *Pair) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_api_proto_api_proto_msgTypes[0] | ||||
| 	if protoimpl.UnsafeEnabled && x != nil { | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		if ms.LoadMessageInfo() == nil { | ||||
| 			ms.StoreMessageInfo(mi) | ||||
| 		} | ||||
| 		return ms | ||||
| 	} | ||||
| 	return mi.MessageOf(x) | ||||
| } | ||||
|  | ||||
| // Deprecated: Use Pair.ProtoReflect.Descriptor instead. | ||||
| func (*Pair) Descriptor() ([]byte, []int) { | ||||
| 	return file_api_proto_api_proto_rawDescGZIP(), []int{0} | ||||
| } | ||||
|  | ||||
| func (x *Pair) GetKey() string { | ||||
| 	if x != nil { | ||||
| 		return x.Key | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (x *Pair) GetValues() []string { | ||||
| 	if x != nil { | ||||
| 		return x.Values | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // A HTTP request as RPC | ||||
| // Forward by the api handler | ||||
| type Request struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
|  | ||||
| 	Method string           `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` | ||||
| 	Path   string           `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` | ||||
| 	Header map[string]*Pair `protobuf:"bytes,3,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||
| 	Get    map[string]*Pair `protobuf:"bytes,4,rep,name=get,proto3" json:"get,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||
| 	Post   map[string]*Pair `protobuf:"bytes,5,rep,name=post,proto3" json:"post,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||
| 	Body   string           `protobuf:"bytes,6,opt,name=body,proto3" json:"body,omitempty"` // raw request body; if not application/x-www-form-urlencoded | ||||
| 	Url    string           `protobuf:"bytes,7,opt,name=url,proto3" json:"url,omitempty"` | ||||
| } | ||||
|  | ||||
| func (x *Request) Reset() { | ||||
| 	*x = Request{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_api_proto_api_proto_msgTypes[1] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (x *Request) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
|  | ||||
| func (*Request) ProtoMessage() {} | ||||
|  | ||||
| func (x *Request) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_api_proto_api_proto_msgTypes[1] | ||||
| 	if protoimpl.UnsafeEnabled && x != nil { | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		if ms.LoadMessageInfo() == nil { | ||||
| 			ms.StoreMessageInfo(mi) | ||||
| 		} | ||||
| 		return ms | ||||
| 	} | ||||
| 	return mi.MessageOf(x) | ||||
| } | ||||
|  | ||||
| // Deprecated: Use Request.ProtoReflect.Descriptor instead. | ||||
| func (*Request) Descriptor() ([]byte, []int) { | ||||
| 	return file_api_proto_api_proto_rawDescGZIP(), []int{1} | ||||
| } | ||||
|  | ||||
| func (x *Request) GetMethod() string { | ||||
| 	if x != nil { | ||||
| 		return x.Method | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (x *Request) GetPath() string { | ||||
| 	if x != nil { | ||||
| 		return x.Path | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (x *Request) GetHeader() map[string]*Pair { | ||||
| 	if x != nil { | ||||
| 		return x.Header | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (x *Request) GetGet() map[string]*Pair { | ||||
| 	if x != nil { | ||||
| 		return x.Get | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (x *Request) GetPost() map[string]*Pair { | ||||
| 	if x != nil { | ||||
| 		return x.Post | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (x *Request) GetBody() string { | ||||
| 	if x != nil { | ||||
| 		return x.Body | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (x *Request) GetUrl() string { | ||||
| 	if x != nil { | ||||
| 		return x.Url | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // A HTTP response as RPC | ||||
| // Expected response for the api handler | ||||
| type Response struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
|  | ||||
| 	StatusCode int32            `protobuf:"varint,1,opt,name=statusCode,proto3" json:"statusCode,omitempty"` | ||||
| 	Header     map[string]*Pair `protobuf:"bytes,2,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||
| 	Body       string           `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"` | ||||
| } | ||||
|  | ||||
| func (x *Response) Reset() { | ||||
| 	*x = Response{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_api_proto_api_proto_msgTypes[2] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (x *Response) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
|  | ||||
| func (*Response) ProtoMessage() {} | ||||
|  | ||||
| func (x *Response) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_api_proto_api_proto_msgTypes[2] | ||||
| 	if protoimpl.UnsafeEnabled && x != nil { | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		if ms.LoadMessageInfo() == nil { | ||||
| 			ms.StoreMessageInfo(mi) | ||||
| 		} | ||||
| 		return ms | ||||
| 	} | ||||
| 	return mi.MessageOf(x) | ||||
| } | ||||
|  | ||||
| // Deprecated: Use Response.ProtoReflect.Descriptor instead. | ||||
| func (*Response) Descriptor() ([]byte, []int) { | ||||
| 	return file_api_proto_api_proto_rawDescGZIP(), []int{2} | ||||
| } | ||||
|  | ||||
| func (x *Response) GetStatusCode() int32 { | ||||
| 	if x != nil { | ||||
| 		return x.StatusCode | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (x *Response) GetHeader() map[string]*Pair { | ||||
| 	if x != nil { | ||||
| 		return x.Header | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (x *Response) GetBody() string { | ||||
| 	if x != nil { | ||||
| 		return x.Body | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| // A HTTP event as RPC | ||||
| // Forwarded by the event handler | ||||
| type Event struct { | ||||
| 	state         protoimpl.MessageState | ||||
| 	sizeCache     protoimpl.SizeCache | ||||
| 	unknownFields protoimpl.UnknownFields | ||||
|  | ||||
| 	// e.g login | ||||
| 	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` | ||||
| 	// uuid | ||||
| 	Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` | ||||
| 	// unix timestamp of event | ||||
| 	Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` | ||||
| 	// event headers | ||||
| 	Header map[string]*Pair `protobuf:"bytes,4,rep,name=header,proto3" json:"header,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` | ||||
| 	// the event data | ||||
| 	Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` | ||||
| } | ||||
|  | ||||
| func (x *Event) Reset() { | ||||
| 	*x = Event{} | ||||
| 	if protoimpl.UnsafeEnabled { | ||||
| 		mi := &file_api_proto_api_proto_msgTypes[3] | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		ms.StoreMessageInfo(mi) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (x *Event) String() string { | ||||
| 	return protoimpl.X.MessageStringOf(x) | ||||
| } | ||||
|  | ||||
| func (*Event) ProtoMessage() {} | ||||
|  | ||||
| func (x *Event) ProtoReflect() protoreflect.Message { | ||||
| 	mi := &file_api_proto_api_proto_msgTypes[3] | ||||
| 	if protoimpl.UnsafeEnabled && x != nil { | ||||
| 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) | ||||
| 		if ms.LoadMessageInfo() == nil { | ||||
| 			ms.StoreMessageInfo(mi) | ||||
| 		} | ||||
| 		return ms | ||||
| 	} | ||||
| 	return mi.MessageOf(x) | ||||
| } | ||||
|  | ||||
| // Deprecated: Use Event.ProtoReflect.Descriptor instead. | ||||
| func (*Event) Descriptor() ([]byte, []int) { | ||||
| 	return file_api_proto_api_proto_rawDescGZIP(), []int{3} | ||||
| } | ||||
|  | ||||
| func (x *Event) GetName() string { | ||||
| 	if x != nil { | ||||
| 		return x.Name | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (x *Event) GetId() string { | ||||
| 	if x != nil { | ||||
| 		return x.Id | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| func (x *Event) GetTimestamp() int64 { | ||||
| 	if x != nil { | ||||
| 		return x.Timestamp | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
|  | ||||
| func (x *Event) GetHeader() map[string]*Pair { | ||||
| 	if x != nil { | ||||
| 		return x.Header | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (x *Event) GetData() string { | ||||
| 	if x != nil { | ||||
| 		return x.Data | ||||
| 	} | ||||
| 	return "" | ||||
| } | ||||
|  | ||||
| var File_api_proto_api_proto protoreflect.FileDescriptor | ||||
|  | ||||
| var file_api_proto_api_proto_rawDesc = []byte{ | ||||
| 	0x0a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2e, | ||||
| 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x22, 0x30, 0x0a, | ||||
| 	0x04, 0x50, 0x61, 0x69, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, | ||||
| 	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, | ||||
| 	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, | ||||
| 	0xc1, 0x03, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6d, | ||||
| 	0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, | ||||
| 	0x68, 0x6f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, | ||||
| 	0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x33, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, | ||||
| 	0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, | ||||
| 	0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, | ||||
| 	0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x03, | ||||
| 	0x67, 0x65, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x2e, 0x61, | ||||
| 	0x70, 0x69, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x6e, | ||||
| 	0x74, 0x72, 0x79, 0x52, 0x03, 0x67, 0x65, 0x74, 0x12, 0x2d, 0x0a, 0x04, 0x70, 0x6f, 0x73, 0x74, | ||||
| 	0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, | ||||
| 	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, | ||||
| 	0x79, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, | ||||
| 	0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, | ||||
| 	0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x1a, 0x47, 0x0a, | ||||
| 	0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, | ||||
| 	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, | ||||
| 	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, | ||||
| 	0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, | ||||
| 	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x44, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x74, | ||||
| 	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, | ||||
| 	0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, | ||||
| 	0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, | ||||
| 	0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x09, | ||||
| 	0x50, 0x6f, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, | ||||
| 	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, | ||||
| 	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, | ||||
| 	0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, | ||||
| 	0x02, 0x38, 0x01, 0x22, 0xbd, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, | ||||
| 	0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, | ||||
| 	0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, | ||||
| 	0x12, 0x34, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, | ||||
| 	0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, | ||||
| 	0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, | ||||
| 	0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, | ||||
| 	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x47, 0x0a, 0x0b, 0x48, 0x65, | ||||
| 	0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, | ||||
| 	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, | ||||
| 	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, | ||||
| 	0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, | ||||
| 	0x02, 0x38, 0x01, 0x22, 0xd9, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, | ||||
| 	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, | ||||
| 	0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, | ||||
| 	0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, | ||||
| 	0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, | ||||
| 	0x31, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, | ||||
| 	0x19, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x48, | ||||
| 	0x65, 0x61, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, | ||||
| 	0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, | ||||
| 	0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x47, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, | ||||
| 	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, | ||||
| 	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, | ||||
| 	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x6f, 0x2e, 0x61, 0x70, 0x69, 0x2e, | ||||
| 	0x50, 0x61, 0x69, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x62, | ||||
| 	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, | ||||
| } | ||||
|  | ||||
| var ( | ||||
| 	file_api_proto_api_proto_rawDescOnce sync.Once | ||||
| 	file_api_proto_api_proto_rawDescData = file_api_proto_api_proto_rawDesc | ||||
| ) | ||||
|  | ||||
| func file_api_proto_api_proto_rawDescGZIP() []byte { | ||||
| 	file_api_proto_api_proto_rawDescOnce.Do(func() { | ||||
| 		file_api_proto_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_api_proto_rawDescData) | ||||
| 	}) | ||||
| 	return file_api_proto_api_proto_rawDescData | ||||
| } | ||||
|  | ||||
| var file_api_proto_api_proto_msgTypes = make([]protoimpl.MessageInfo, 9) | ||||
| var file_api_proto_api_proto_goTypes = []interface{}{ | ||||
| 	(*Pair)(nil),     // 0: go.api.Pair | ||||
| 	(*Request)(nil),  // 1: go.api.Request | ||||
| 	(*Response)(nil), // 2: go.api.Response | ||||
| 	(*Event)(nil),    // 3: go.api.Event | ||||
| 	nil,              // 4: go.api.Request.HeaderEntry | ||||
| 	nil,              // 5: go.api.Request.GetEntry | ||||
| 	nil,              // 6: go.api.Request.PostEntry | ||||
| 	nil,              // 7: go.api.Response.HeaderEntry | ||||
| 	nil,              // 8: go.api.Event.HeaderEntry | ||||
| } | ||||
| var file_api_proto_api_proto_depIdxs = []int32{ | ||||
| 	4,  // 0: go.api.Request.header:type_name -> go.api.Request.HeaderEntry | ||||
| 	5,  // 1: go.api.Request.get:type_name -> go.api.Request.GetEntry | ||||
| 	6,  // 2: go.api.Request.post:type_name -> go.api.Request.PostEntry | ||||
| 	7,  // 3: go.api.Response.header:type_name -> go.api.Response.HeaderEntry | ||||
| 	8,  // 4: go.api.Event.header:type_name -> go.api.Event.HeaderEntry | ||||
| 	0,  // 5: go.api.Request.HeaderEntry.value:type_name -> go.api.Pair | ||||
| 	0,  // 6: go.api.Request.GetEntry.value:type_name -> go.api.Pair | ||||
| 	0,  // 7: go.api.Request.PostEntry.value:type_name -> go.api.Pair | ||||
| 	0,  // 8: go.api.Response.HeaderEntry.value:type_name -> go.api.Pair | ||||
| 	0,  // 9: go.api.Event.HeaderEntry.value:type_name -> go.api.Pair | ||||
| 	10, // [10:10] is the sub-list for method output_type | ||||
| 	10, // [10:10] is the sub-list for method input_type | ||||
| 	10, // [10:10] is the sub-list for extension type_name | ||||
| 	10, // [10:10] is the sub-list for extension extendee | ||||
| 	0,  // [0:10] is the sub-list for field type_name | ||||
| } | ||||
|  | ||||
| func init() { file_api_proto_api_proto_init() } | ||||
| func file_api_proto_api_proto_init() { | ||||
| 	if File_api_proto_api_proto != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	if !protoimpl.UnsafeEnabled { | ||||
| 		file_api_proto_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*Pair); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_api_proto_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*Request); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_api_proto_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*Response); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 		file_api_proto_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { | ||||
| 			switch v := v.(*Event); i { | ||||
| 			case 0: | ||||
| 				return &v.state | ||||
| 			case 1: | ||||
| 				return &v.sizeCache | ||||
| 			case 2: | ||||
| 				return &v.unknownFields | ||||
| 			default: | ||||
| 				return nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	type x struct{} | ||||
| 	out := protoimpl.TypeBuilder{ | ||||
| 		File: protoimpl.DescBuilder{ | ||||
| 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(), | ||||
| 			RawDescriptor: file_api_proto_api_proto_rawDesc, | ||||
| 			NumEnums:      0, | ||||
| 			NumMessages:   9, | ||||
| 			NumExtensions: 0, | ||||
| 			NumServices:   0, | ||||
| 		}, | ||||
| 		GoTypes:           file_api_proto_api_proto_goTypes, | ||||
| 		DependencyIndexes: file_api_proto_api_proto_depIdxs, | ||||
| 		MessageInfos:      file_api_proto_api_proto_msgTypes, | ||||
| 	}.Build() | ||||
| 	File_api_proto_api_proto = out.File | ||||
| 	file_api_proto_api_proto_rawDesc = nil | ||||
| 	file_api_proto_api_proto_goTypes = nil | ||||
| 	file_api_proto_api_proto_depIdxs = nil | ||||
| } | ||||
| @@ -1,21 +0,0 @@ | ||||
| // Code generated by protoc-gen-micro. DO NOT EDIT. | ||||
| // source: api/proto/api.proto | ||||
|  | ||||
| package go_api | ||||
|  | ||||
| import ( | ||||
| 	fmt "fmt" | ||||
| 	proto "github.com/golang/protobuf/proto" | ||||
| 	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.ProtoPackageIsVersion3 // please upgrade the proto package | ||||
| @@ -1,43 +0,0 @@ | ||||
| syntax = "proto3"; | ||||
|  | ||||
| package go.api; | ||||
|  | ||||
| message Pair { | ||||
| 	string key = 1; | ||||
| 	repeated string values = 2; | ||||
| } | ||||
|  | ||||
| // A HTTP request as RPC | ||||
| // Forward by the api handler | ||||
| message Request { | ||||
|         string method = 1; | ||||
|         string path = 2; | ||||
|         map<string, Pair> header = 3; | ||||
|         map<string, Pair> get = 4; | ||||
|         map<string, Pair> post = 5; | ||||
|         string body = 6;  // raw request body; if not application/x-www-form-urlencoded | ||||
| 	string url = 7; | ||||
| } | ||||
|  | ||||
| // A HTTP response as RPC | ||||
| // Expected response for the api handler | ||||
| message Response { | ||||
|         int32 statusCode = 1; | ||||
|         map<string, Pair> header = 2; | ||||
|         string body = 3; | ||||
| } | ||||
|  | ||||
| // A HTTP event as RPC | ||||
| // Forwarded by the event handler | ||||
| message Event { | ||||
| 	// e.g login | ||||
| 	string name = 1; | ||||
| 	// uuid | ||||
| 	string id = 2; | ||||
| 	// unix timestamp of event | ||||
| 	int64 timestamp = 3; | ||||
| 	// event headers | ||||
|         map<string, Pair> header = 4; | ||||
| 	// the event data | ||||
| 	string data = 5; | ||||
| } | ||||
| @@ -6,8 +6,6 @@ import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/unistack-org/micro/v3/api/resolver/vpath" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestResolve(t *testing.T) { | ||||
| @@ -62,9 +60,13 @@ func TestResolve(t *testing.T) { | ||||
| 		t.Run(tc.Name, func(t *testing.T) { | ||||
| 			r := NewResolver(vpath.NewResolver()) | ||||
| 			result, err := r.Resolve(&http.Request{URL: &url.URL{Host: tc.Host, Path: "foo/bar"}}) | ||||
| 			assert.Nil(t, err, "Expecter err to be nil") | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 			if result != nil { | ||||
| 				assert.Equal(t, tc.Result, result.Domain, "Expected %v but got %v", tc.Result, result.Domain) | ||||
| 				if tc.Result != result.Domain { | ||||
| 					t.Fatalf("Expected %v but got %v", tc.Result, result.Domain) | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
|   | ||||
| @@ -7,6 +7,10 @@ import ( | ||||
| 	"github.com/unistack-org/micro/v3/api" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	DefaultRouter Router | ||||
| ) | ||||
|  | ||||
| // Router is used to determine an endpoint for a request | ||||
| type Router interface { | ||||
| 	// Returns options | ||||
| @@ -23,6 +27,6 @@ type Router interface { | ||||
| 	Deregister(ep *api.Endpoint) error | ||||
| 	// Route returns an api.Service route | ||||
| 	Route(r *http.Request) (*api.Service, error) | ||||
| 	// String represenation of router | ||||
| 	// String representation of router | ||||
| 	String() string | ||||
| } | ||||
|   | ||||
| @@ -235,6 +235,7 @@ func (m *memorySubscriber) Unsubscribe(ctx context.Context) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NewBroker return new memory broker | ||||
| func NewBroker(opts ...Option) Broker { | ||||
| 	rand.Seed(time.Now().UnixNano()) | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import ( | ||||
| var ( | ||||
| 	// DefaultClient is the global default client | ||||
| 	DefaultClient      Client = NewClient() | ||||
| 	DefaultContentType        = "application/json" | ||||
| ) | ||||
|  | ||||
| // Client is the interface used to make requests to services. | ||||
|   | ||||
| @@ -20,10 +20,6 @@ var ( | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	defaultContentType = "application/json" | ||||
| ) | ||||
|  | ||||
| type noopClient struct { | ||||
| 	opts Options | ||||
| } | ||||
|   | ||||
| @@ -151,7 +151,7 @@ type RequestOptions struct { | ||||
| func NewOptions(opts ...Option) Options { | ||||
| 	options := Options{ | ||||
| 		Context:     context.Background(), | ||||
| 		ContentType: "application/json", | ||||
| 		ContentType: DefaultContentType, | ||||
| 		Codecs:      make(map[string]codec.Codec), | ||||
| 		CallOptions: CallOptions{ | ||||
| 			Backoff:        DefaultBackoff, | ||||
|   | ||||
| @@ -8,8 +8,8 @@ import ( | ||||
| 	"github.com/unistack-org/micro/v3/metadata" | ||||
| ) | ||||
|  | ||||
| // Message types | ||||
| const ( | ||||
| 	// Message types | ||||
| 	Error MessageType = iota | ||||
| 	Request | ||||
| 	Response | ||||
| @@ -26,6 +26,7 @@ var ( | ||||
| var ( | ||||
| 	// DefaultMaxMsgSize specifies how much data codec can handle | ||||
| 	DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb | ||||
| 	// DefaultCodec is the global default codec | ||||
| 	DefaultCodec Codec = NewCodec() | ||||
| ) | ||||
|  | ||||
|   | ||||
| @@ -30,12 +30,8 @@ func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error { | ||||
| 	} | ||||
|  | ||||
| 	switch v := b.(type) { | ||||
| 	case string: | ||||
| 		v = string(buf) | ||||
| 	case *string: | ||||
| 		*v = string(buf) | ||||
| 	case []byte: | ||||
| 		v = buf | ||||
| 	case *[]byte: | ||||
| 		*v = buf | ||||
| 	case *Frame: | ||||
| @@ -112,15 +108,9 @@ func (c *noopCodec) Unmarshal(d []byte, v interface{}) error { | ||||
| 		return nil | ||||
| 	} | ||||
| 	switch ve := v.(type) { | ||||
| 	case string: | ||||
| 		ve = string(d) | ||||
| 		return nil | ||||
| 	case *string: | ||||
| 		*ve = string(d) | ||||
| 		return nil | ||||
| 	case []byte: | ||||
| 		ve = d | ||||
| 		return nil | ||||
| 	case *[]byte: | ||||
| 		*ve = d | ||||
| 		return nil | ||||
|   | ||||
							
								
								
									
										34
									
								
								codec/noop_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								codec/noop_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| package codec | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestNoopBytes(t *testing.T) { | ||||
| 	req := []byte("test req") | ||||
| 	rsp := make([]byte, len(req)) | ||||
|  | ||||
| 	nc := NewCodec() | ||||
| 	if err := nc.Unmarshal(req, &rsp); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if !bytes.Equal(req, rsp) { | ||||
| 		t.Fatalf("req not eq rsp: %s != %s", req, rsp) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNoopString(t *testing.T) { | ||||
| 	req := []byte("test req") | ||||
| 	var rsp string | ||||
|  | ||||
| 	nc := NewCodec() | ||||
| 	if err := nc.Unmarshal(req, &rsp); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	if !bytes.Equal(req, []byte(rsp)) { | ||||
| 		t.Fatalf("req not eq rsp: %s != %s", req, rsp) | ||||
| 	} | ||||
| } | ||||
| @@ -45,6 +45,7 @@ func Meter(m meter.Meter) Option { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NewOptions returns new options | ||||
| func NewOptions(opts ...Option) Options { | ||||
| 	options := Options{ | ||||
| 		Logger:     logger.DefaultLogger, | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import ( | ||||
|  | ||||
| type configKey struct{} | ||||
|  | ||||
| // FromContext returns store from context | ||||
| func FromContext(ctx context.Context) (Config, bool) { | ||||
| 	if ctx == nil { | ||||
| 		return nil, false | ||||
| @@ -14,6 +15,7 @@ func FromContext(ctx context.Context) (Config, bool) { | ||||
| 	return c, ok | ||||
| } | ||||
|  | ||||
| // NewContext put store in context | ||||
| func NewContext(ctx context.Context, c Config) context.Context { | ||||
| 	if ctx == nil { | ||||
| 		ctx = context.Background() | ||||
|   | ||||
| @@ -64,7 +64,7 @@ func newFunction(opts ...Option) Function { | ||||
| 	// make context the last thing | ||||
| 	fopts = append(fopts, Context(ctx)) | ||||
|  | ||||
| 	service := &service{opts: NewOptions(opts...)} | ||||
| 	service := &service{opts: NewOptions(fopts...)} | ||||
|  | ||||
| 	fn := &function{ | ||||
| 		cancel:  cancel, | ||||
|   | ||||
							
								
								
									
										6
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.mod
									
									
									
									
									
								
							| @@ -6,18 +6,12 @@ require ( | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
| 	github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||
| 	github.com/ef-ds/deque v1.0.4 | ||||
| 	github.com/golang/protobuf v1.4.3 | ||||
| 	github.com/google/uuid v1.2.0 | ||||
| 	github.com/imdario/mergo v0.3.11 | ||||
| 	github.com/kr/text v0.2.0 // indirect | ||||
| 	github.com/miekg/dns v1.1.38 | ||||
| 	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect | ||||
| 	github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c | ||||
| 	github.com/patrickmn/go-cache v2.1.0+incompatible | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect | ||||
| 	golang.org/x/net v0.0.0-20210119194325-5f4716e94777 | ||||
| 	golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect | ||||
| 	google.golang.org/protobuf v1.25.0 | ||||
| 	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										88
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,8 +1,3 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||||
| github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| @@ -11,26 +6,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC | ||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||||
| github.com/ef-ds/deque v1.0.4 h1:iFAZNmveMT9WERAkqLJ+oaABF9AcVQ5AjXem/hroniI= | ||||
| github.com/ef-ds/deque v1.0.4/go.mod h1:gXDnTC3yqvBcHbq2lcExjtAcVrOnJCbMcZXmuj8Z4tg= | ||||
| github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||
| github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= | ||||
| github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= | ||||
| github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= | ||||
| github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= | ||||
| @@ -39,84 +14,23 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/miekg/dns v1.1.38 h1:MtIY+fmHUVVgv1AXzmKMWcwdCYxTRPG1EDjpqF4RCEw= | ||||
| github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= | ||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= | ||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | ||||
| github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= | ||||
| github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= | ||||
| github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= | ||||
| github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= | ||||
| golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= | ||||
| golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= | ||||
| golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= | ||||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | ||||
| golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= | ||||
| google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= | ||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| @@ -124,5 +38,3 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= | ||||
| gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
|   | ||||
| @@ -2,19 +2,16 @@ package meter | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestNoopMeter(t *testing.T) { | ||||
| 	meter := NewMeter(Path("/noop")) | ||||
| 	assert.NotNil(t, meter) | ||||
| 	assert.Equal(t, "/noop", meter.Options().Path) | ||||
| 	assert.Implements(t, new(Meter), meter) | ||||
| 	if "/noop" != meter.Options().Path { | ||||
| 		t.Fatalf("invalid options parsing: %v", meter.Options()) | ||||
| 	} | ||||
|  | ||||
| 	cnt := meter.Counter("counter", Label("server", "noop")) | ||||
| 	cnt.Inc() | ||||
|  | ||||
| } | ||||
|  | ||||
| func TestLabels(t *testing.T) { | ||||
|   | ||||
| @@ -4,8 +4,9 @@ package dns | ||||
| import ( | ||||
| 	"context" | ||||
| 	"net" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/miekg/dns" | ||||
| 	"github.com/unistack-org/micro/v3/resolver" | ||||
| ) | ||||
|  | ||||
| @@ -13,9 +14,10 @@ import ( | ||||
| type Resolver struct { | ||||
| 	// The resolver address to use | ||||
| 	Address    string | ||||
| 	goresolver *net.Resolver | ||||
| 	sync.RWMutex | ||||
| } | ||||
|  | ||||
| // Resolve assumes ID is a domain name e.g micro.mu | ||||
| func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) { | ||||
| 	host, port, err := net.SplitHostPort(name) | ||||
| 	if err != nil { | ||||
| @@ -28,56 +30,46 @@ func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) { | ||||
| 	} | ||||
|  | ||||
| 	if len(r.Address) == 0 { | ||||
| 		r.Address = "1.0.0.1:53" | ||||
| 		r.Address = "1.1.1.1:53" | ||||
| 	} | ||||
|  | ||||
| 	//nolint:prealloc | ||||
| 	var records []*resolver.Record | ||||
|  | ||||
| 	// parsed an actual ip | ||||
| 	if v := net.ParseIP(host); v != nil { | ||||
| 		records = append(records, &resolver.Record{ | ||||
| 			Address: net.JoinHostPort(host, port), | ||||
| 		}) | ||||
| 		return records, nil | ||||
| 		rec := &resolver.Record{Address: net.JoinHostPort(host, port)} | ||||
| 		return []*resolver.Record{rec}, nil | ||||
| 	} | ||||
|  | ||||
| 	for _, q := range []uint16{dns.TypeA, dns.TypeAAAA} { | ||||
| 		m := new(dns.Msg) | ||||
| 		m.SetQuestion(dns.Fqdn(host), q) | ||||
| 		rec, err := dns.ExchangeContext(context.Background(), m, r.Address) | ||||
| 	r.RLock() | ||||
| 	goresolver := r.goresolver | ||||
| 	r.RUnlock() | ||||
|  | ||||
| 	if goresolver == nil { | ||||
| 		r.Lock() | ||||
| 		r.goresolver = &net.Resolver{ | ||||
| 			Dial: func(ctx context.Context, network, address string) (net.Conn, error) { | ||||
| 				d := net.Dialer{ | ||||
| 					Timeout: time.Millisecond * time.Duration(100), | ||||
| 				} | ||||
| 				return d.DialContext(ctx, "udp", r.Address) | ||||
| 			}, | ||||
| 		} | ||||
| 		r.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	addrs, err := goresolver.LookupIP(context.TODO(), "ip", host) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 		var addr string | ||||
| 		for _, answer := range rec.Answer { | ||||
| 			h := answer.Header() | ||||
| 			// check record type matches | ||||
| 			switch h.Rrtype { | ||||
| 			case dns.TypeA: | ||||
| 				arec, _ := answer.(*dns.A) | ||||
| 				addr = arec.A.String() | ||||
| 			case dns.TypeAAAA: | ||||
| 				arec, _ := answer.(*dns.AAAA) | ||||
| 				addr = arec.AAAA.String() | ||||
| 			default: | ||||
| 				continue | ||||
| 	if len(addrs) == 0 { | ||||
| 		rec := &resolver.Record{Address: net.JoinHostPort(host, port)} | ||||
| 		return []*resolver.Record{rec}, nil | ||||
| 	} | ||||
|  | ||||
| 			// join resolved record with port | ||||
| 			address := net.JoinHostPort(addr, port) | ||||
| 			// append to record set | ||||
| 	records := make([]*resolver.Record, 0, len(addrs)) | ||||
| 	for _, addr := range addrs { | ||||
| 		records = append(records, &resolver.Record{ | ||||
| 				Address: address, | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// no records returned so just best effort it | ||||
| 	if len(records) == 0 { | ||||
| 		records = append(records, &resolver.Record{ | ||||
| 			Address: net.JoinHostPort(host, port), | ||||
| 			Address: net.JoinHostPort(addr.String(), port), | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										14
									
								
								resolver/dns/dns_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								resolver/dns/dns_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| package dns | ||||
|  | ||||
| import "testing" | ||||
|  | ||||
| func TestResolver(t *testing.T) { | ||||
| 	r := &Resolver{} | ||||
| 	recs, err := r.Resolve("unistack.org") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if len(recs) < 1 { | ||||
| 		t.Fatalf("records not resolved: %v", recs) | ||||
| 	} | ||||
| } | ||||
| @@ -3,7 +3,6 @@ package roundrobin | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"github.com/unistack-org/micro/v3/selector" | ||||
| ) | ||||
|  | ||||
| @@ -19,18 +18,29 @@ func TestRoundRobin(t *testing.T) { | ||||
| 	// By passing r1 and r2 first, it forces a set sequence of (r1 => r2 => r3 => r1) | ||||
|  | ||||
| 	next, err := sel.Select([]string{r1}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	r := next() | ||||
| 	assert.Nil(t, err, "Error should be nil") | ||||
| 	assert.Equal(t, r1, r, "Expected route to be r1") | ||||
|  | ||||
| 	if r1 != r { | ||||
| 		t.Fatal("Expected route to be r == r1") | ||||
| 	} | ||||
|  | ||||
| 	next, err = sel.Select([]string{r2}) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	r = next() | ||||
| 	assert.Nil(t, err, "Error should be nil") | ||||
| 	assert.Equal(t, r2, r, "Expected route to be r2") | ||||
| 	if r2 != r { | ||||
| 		t.Fatal("Expected route to be r2") | ||||
| 	} | ||||
|  | ||||
| 	routes := []string{r1, r2, r3} | ||||
| 	next, err = sel.Select(routes) | ||||
| 	assert.Nil(t, err, "Error should be nil") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	n1, n2, n3, n4 := next(), next(), next(), next() | ||||
|  | ||||
| 	// start element is random but then it should loop through in order | ||||
| @@ -41,9 +51,19 @@ func TestRoundRobin(t *testing.T) { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	assert.NotEqual(t, start, -1) | ||||
| 	assert.Equal(t, routes[start], n1, "Unexpected route") | ||||
| 	assert.Equal(t, routes[(start+1)%3], n2, "Unexpected route") | ||||
| 	assert.Equal(t, routes[(start+2)%3], n3, "Unexpected route") | ||||
| 	assert.Equal(t, routes[(start+3)%3], n4, "Unexpected route") | ||||
| 	if start == -1 { | ||||
| 		t.Fatalf("start == -1 %v %v", start, -1) | ||||
| 	} | ||||
| 	if routes[start] != n1 { | ||||
| 		t.Fatal("Unexpected route") | ||||
| 	} | ||||
| 	if routes[(start+1)%3] != n2 { | ||||
| 		t.Fatal("Unexpected route") | ||||
| 	} | ||||
| 	if routes[(start+2)%3] != n3 { | ||||
| 		t.Fatal("Unexpected route") | ||||
| 	} | ||||
| 	if routes[(start+3)%3] != n4 { | ||||
| 		t.Fatal("Unexpected route") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -2,8 +2,6 @@ package selector | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| // Tests runs all the tests against a selector to ensure the implementations are consistent | ||||
| @@ -14,19 +12,27 @@ func Tests(t *testing.T, s Selector) { | ||||
| 	t.Run("Select", func(t *testing.T) { | ||||
| 		t.Run("NoRoutes", func(t *testing.T) { | ||||
| 			_, err := s.Select([]string{}) | ||||
| 			assert.Equal(t, ErrNoneAvailable, err, "Expected error to be none available") | ||||
| 			if err != ErrNoneAvailable { | ||||
| 				t.Fatal("Expected error to be none available") | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("OneRoute", func(t *testing.T) { | ||||
| 			next, err := s.Select([]string{r1}) | ||||
| 			if err != nil { | ||||
| 				t.Fatal("Error should be nil") | ||||
| 			} | ||||
| 			srv := next() | ||||
| 			assert.Nil(t, err, "Error should be nil") | ||||
| 			assert.Equal(t, r1, srv, "Expected the route to be returned") | ||||
| 			if r1 != srv { | ||||
| 				t.Fatal("Expected the route to be returned") | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("MultipleRoutes", func(t *testing.T) { | ||||
| 			next, err := s.Select([]string{r1, r2}) | ||||
| 			assert.Nil(t, err, "Error should be nil") | ||||
| 			if err != nil { | ||||
| 				t.Fatal("Error should be nil") | ||||
| 			} | ||||
| 			srv := next() | ||||
| 			if srv != r1 && srv != r2 { | ||||
| 				t.Errorf("Expected the route to be one of the inputs") | ||||
| @@ -35,11 +41,14 @@ func Tests(t *testing.T, s Selector) { | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Record", func(t *testing.T) { | ||||
| 		err := s.Record(r1, nil) | ||||
| 		assert.Nil(t, err, "Expected the error to be nil") | ||||
| 		if err := s.Record(r1, nil); err != nil { | ||||
| 			t.Fatal("Expected the error to be nil") | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("String", func(t *testing.T) { | ||||
| 		assert.NotEmpty(t, s.String(), "String returned a blank string") | ||||
| 		if s.String() == "" { | ||||
| 			t.Fatal("String returned a blank string") | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,6 @@ package store_test | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"os" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| @@ -53,9 +52,6 @@ func TestMemoryNamespacePrefix(t *testing.T) { | ||||
|  | ||||
| func basictest(s store.Store, t *testing.T) { | ||||
| 	ctx := context.Background() | ||||
| 	if len(os.Getenv("IN_TRAVIS_CI")) == 0 { | ||||
| 		t.Logf("Testing store %s, with options %#+v\n", s.String(), s.Options()) | ||||
| 	} | ||||
| 	// Read and Write an expiring Record | ||||
| 	if err := s.Write(ctx, "Hello", "World", store.WriteTTL(time.Millisecond*100)); err != nil { | ||||
| 		t.Error(err) | ||||
| @@ -71,5 +67,7 @@ func basictest(s store.Store, t *testing.T) { | ||||
| 		t.Errorf("Expected %# v, got %# v", store.ErrNotFound, err) | ||||
| 	} | ||||
|  | ||||
| 	s.Disconnect(ctx) // reset the store | ||||
| 	if err := s.Disconnect(ctx); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -60,10 +60,9 @@ func NewRoundTripper(opts ...Option) http.RoundTripper { | ||||
| // RequestToContext puts the `Authorization` header bearer token into context | ||||
| // so calls to services will be authorized. | ||||
| func RequestToContext(r *http.Request) context.Context { | ||||
| 	ctx := context.Background() | ||||
| 	md := make(metadata.Metadata) | ||||
| 	md := metadata.New(len(r.Header)) | ||||
| 	for k, v := range r.Header { | ||||
| 		md[k] = strings.Join(v, ",") | ||||
| 		md.Set(k, strings.Join(v, ",")) | ||||
| 	} | ||||
| 	return metadata.NewContext(ctx, md) | ||||
| 	return metadata.NewIncomingContext(r.Context(), md) | ||||
| } | ||||
|   | ||||
| @@ -85,12 +85,12 @@ func CSR(opts ...CertOption) ([]byte, error) { | ||||
| } | ||||
|  | ||||
| // Sign decodes a CSR and signs it with the CA | ||||
| func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) { | ||||
| func Sign(crt, key, csr []byte, opts ...CertOption) ([]byte, error) { | ||||
| 	options := CertOptions{} | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
| 	asn1CACrt, err := decodePEM(CACrt) | ||||
| 	asn1CACrt, err := decodePEM(crt) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to decode CA Crt PEM: %w", err) | ||||
| 	} | ||||
| @@ -101,7 +101,7 @@ func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) { | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("ca is not a valid certificate: %w", err) | ||||
| 	} | ||||
| 	asn1CAKey, err := decodePEM(CAKey) | ||||
| 	asn1CAKey, err := decodePEM(key) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to decode CA  Key PEM: %w", err) | ||||
| 	} | ||||
| @@ -112,22 +112,22 @@ func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) { | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("ca key is not a valid private key: %w", err) | ||||
| 	} | ||||
| 	asn1CSR, err := decodePEM(CSR) | ||||
| 	asn1CSR, err := decodePEM(csr) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("failed to decode CSR PEM: %w", err) | ||||
| 	} | ||||
| 	if len(asn1CSR) != 1 { | ||||
| 		return nil, fmt.Errorf("expected 1 CSR, got %d", len(asn1CSR)) | ||||
| 	} | ||||
| 	csr, err := x509.ParseCertificateRequest(asn1CSR[0].Bytes) | ||||
| 	caCsr, err := x509.ParseCertificateRequest(asn1CSR[0].Bytes) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("csr is invalid: %w", err) | ||||
| 	} | ||||
| 	template := &x509.Certificate{ | ||||
| 		SignatureAlgorithm:    x509.PureEd25519, | ||||
| 		Subject:               csr.Subject, | ||||
| 		DNSNames:              csr.DNSNames, | ||||
| 		IPAddresses:           csr.IPAddresses, | ||||
| 		Subject:               caCsr.Subject, | ||||
| 		DNSNames:              caCsr.DNSNames, | ||||
| 		IPAddresses:           caCsr.IPAddresses, | ||||
| 		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, | ||||
| 		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||||
| 		NotBefore:             options.NotBefore, | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| package pki | ||||
|  | ||||
| import ( | ||||
| 	"crypto/ed25519" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/x509" | ||||
| 	"crypto/x509/pkix" | ||||
| @@ -10,22 +9,26 @@ import ( | ||||
| 	"net" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestPrivateKey(t *testing.T) { | ||||
| 	_, _, err := GenerateKey() | ||||
| 	assert.NoError(t, err) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestCA(t *testing.T) { | ||||
| 	pub, priv, err := GenerateKey() | ||||
| 	assert.NoError(t, err) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	serialNumberMax := new(big.Int).Lsh(big.NewInt(1), 128) | ||||
| 	serialNumber, err := rand.Int(rand.Reader, serialNumberMax) | ||||
| 	assert.NoError(t, err, "Couldn't generate serial") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	cert, key, err := CA( | ||||
| 		KeyPair(pub, priv), | ||||
| @@ -38,31 +41,57 @@ func TestCA(t *testing.T) { | ||||
| 		NotBefore(time.Now().Add(time.Minute*-1)), | ||||
| 		NotAfter(time.Now().Add(time.Minute)), | ||||
| 	) | ||||
| 	assert.NoError(t, err, "Couldn't sign CA") | ||||
| 	asn1Key, _ := pem.Decode(key) | ||||
| 	assert.NotNil(t, asn1Key, "Couldn't decode key") | ||||
| 	assert.Equal(t, "PRIVATE KEY", asn1Key.Type) | ||||
| 	decodedKey, err := x509.ParsePKCS8PrivateKey(asn1Key.Bytes) | ||||
| 	assert.NoError(t, err, "Couldn't decode ASN1 Key") | ||||
| 	assert.Equal(t, priv, decodedKey.(ed25519.PrivateKey)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	pool := x509.NewCertPool() | ||||
| 	assert.True(t, pool.AppendCertsFromPEM(cert), "Coudn't parse cert") | ||||
| 	asn1Key, _ := pem.Decode(key) | ||||
| 	if asn1Key == nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	if asn1Key.Type != "PRIVATE KEY" { | ||||
| 		t.Fatal("invalid key type") | ||||
| 	} | ||||
| 	decodedKey, err := x509.ParsePKCS8PrivateKey(asn1Key.Bytes) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} else if decodedKey == nil { | ||||
| 		t.Fatal("empty key") | ||||
| 	} | ||||
|  | ||||
| 	asn1Cert, _ := pem.Decode(cert) | ||||
| 	assert.NotNil(t, asn1Cert, "Couldn't parse pem cert") | ||||
| 	if asn1Cert == nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 		pool := x509.NewCertPool() | ||||
|  | ||||
| 		x509cert, err := x509.ParseCertificate(asn1Cert.Bytes) | ||||
| 	assert.NoError(t, err, "Couldn't parse asn1 cert") | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		chains, err := x509cert.Verify(x509.VerifyOptions{ | ||||
| 			Roots: pool, | ||||
| 		}) | ||||
| 	assert.NoError(t, err, "Cert didn't verify") | ||||
| 	assert.Len(t, chains, 1, "CA should have 1 cert in chain") | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		if len(chains) != 1 { | ||||
| 			t.Fatal("CA should have 1 cert in chain") | ||||
| 		} | ||||
| 	*/ | ||||
| } | ||||
|  | ||||
| func TestCSR(t *testing.T) { | ||||
| 	pub, priv, err := GenerateKey() | ||||
| 	assert.NoError(t, err) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	csr, err := CSR( | ||||
| 		Subject( | ||||
| 			pkix.Name{ | ||||
| @@ -75,16 +104,26 @@ func TestCSR(t *testing.T) { | ||||
| 		IPAddresses(net.ParseIP("127.0.0.1")), | ||||
| 		KeyPair(pub, priv), | ||||
| 	) | ||||
| 	assert.NoError(t, err, "CSR couldn't be encoded") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	asn1csr, _ := pem.Decode(csr) | ||||
| 	assert.NotNil(t, asn1csr) | ||||
| 	if asn1csr == nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	decodedcsr, err := x509.ParseCertificateRequest(asn1csr.Bytes) | ||||
| 	assert.NoError(t, err) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	expected := pkix.Name{ | ||||
| 		CommonName:         "testnode", | ||||
| 		Organization:       []string{"microtest"}, | ||||
| 		OrganizationalUnit: []string{"super-testers"}, | ||||
| 	} | ||||
| 	assert.Equal(t, decodedcsr.Subject.String(), expected.String()) | ||||
| 	if decodedcsr.Subject.String() != expected.String() { | ||||
| 		t.Fatalf("%s != %s", decodedcsr.Subject.String(), expected.String()) | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user