1
0
mirror of https://github.com/go-micro/go-micro.git synced 2025-01-23 17:53:05 +02:00

no one is using that

This commit is contained in:
asim 2024-07-07 18:40:15 +01:00
parent bac34aaec1
commit 3d5f87c01b
62 changed files with 0 additions and 7769 deletions

View File

@ -1,184 +0,0 @@
// Package api is for building api gateways
package api
import (
"context"
"errors"
"regexp"
"strings"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/client"
"go-micro.dev/v5/registry"
"go-micro.dev/v5/server"
)
// API interface provides a way to
// create composable API gateways.
type Api interface {
// Initialize options
Init(...Option) error
// Get the options
Options() Options
// Register an endpoint
Register(*Endpoint) error
// Deregister an endpoint
Deregister(*Endpoint) error
// Run the api
Run(context.Context) error
// Implemenation of api e.g http
String() string
}
// Options are API options.
type Options struct {
// Router for resolving routes
Router router.Router
// Client to use for RPC
Client client.Client
// Address of the server
Address string
}
// Option type are API option args.
type Option func(*Options) error
// Endpoint is a mapping between an RPC method and HTTP endpoint.
type Endpoint struct {
// RPC Method e.g. Greeter.Hello
Name string
// Description e.g what's this endpoint for
Description string
// API Handler e.g rpc, proxy
Handler string
// HTTP Host e.g example.com
Host []string
// HTTP Methods e.g GET, POST
Method []string
// HTTP Path e.g /greeter. Expect POSIX regex
Path []string
// Stream flag
Stream bool
}
// Service represents an API service.
type Service struct {
// Name of service
Name string
// The endpoint for this service
Endpoint *Endpoint
// Versions of this service
Versions []*registry.Service
}
func strip(s string) string {
return strings.TrimSpace(s)
}
func slice(s string) []string {
var sl []string
for _, p := range strings.Split(s, ",") {
if str := strip(p); len(str) > 0 {
sl = append(sl, strip(p))
}
}
return sl
}
// Encode encodes an endpoint to endpoint metadata.
func Encode(e *Endpoint) map[string]string {
if e == nil {
return nil
}
// endpoint map
em := make(map[string]string)
// set vals only if they exist
set := func(k, v string) {
if len(v) == 0 {
return
}
em[k] = v
}
set("endpoint", e.Name)
set("description", e.Description)
set("handler", e.Handler)
set("method", strings.Join(e.Method, ","))
set("path", strings.Join(e.Path, ","))
set("host", strings.Join(e.Host, ","))
return em
}
// Decode decodes endpoint metadata into an endpoint.
func Decode(e map[string]string) *Endpoint {
if e == nil {
return nil
}
return &Endpoint{
Name: e["endpoint"],
Description: e["description"],
Method: slice(e["method"]),
Path: slice(e["path"]),
Host: slice(e["host"]),
Handler: e["handler"],
}
}
// Validate validates an endpoint to guarantee it won't blow up when being served.
func Validate(e *Endpoint) error {
if e == nil {
return errors.New("endpoint is nil")
}
if len(e.Name) == 0 {
return errors.New("name required")
}
for _, p := range e.Path {
ps := p[0]
pe := p[len(p)-1]
if ps == '^' && pe == '$' {
_, err := regexp.CompilePOSIX(p)
if err != nil {
return err
}
} else if ps == '^' && pe != '$' {
return errors.New("invalid path")
} else if ps != '^' && pe == '$' {
return errors.New("invalid path")
}
}
if len(e.Handler) == 0 {
return errors.New("invalid handler")
}
return nil
}
// WithEndpoint returns a server.HandlerOption with endpoint metadata set
//
// Usage:
//
// proto.RegisterHandler(service.Server(), new(Handler), api.WithEndpoint(
// &api.Endpoint{
// Name: "Greeter.Hello",
// Path: []string{"/greeter"},
// },
// ))
func WithEndpoint(e *Endpoint) server.HandlerOption {
return server.EndpointMetadata(e.Name, Encode(e))
}
// NewApi returns a new api gateway.
func NewApi(opts ...Option) Api {
return newApi(opts...)
}

View File

@ -1,151 +0,0 @@
package api
import (
"strings"
"testing"
)
func TestEncoding(t *testing.T) {
testData := []*Endpoint{
nil,
{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test"},
},
}
compare := func(expect, got []string) bool {
// no data to compare, return true
if len(expect) == 0 && len(got) == 0 {
return true
}
// no data expected but got some return false
if len(expect) == 0 && len(got) > 0 {
return false
}
// compare expected with what we got
for _, e := range expect {
var seen bool
for _, g := range got {
if e == g {
seen = true
break
}
}
if !seen {
return false
}
}
// we're done, return true
return true
}
for _, d := range testData {
// encode
e := Encode(d)
// decode
de := Decode(e)
// nil endpoint returns nil
if d == nil {
if e != nil {
t.Fatalf("expected nil got %v", e)
}
if de != nil {
t.Fatalf("expected nil got %v", de)
}
continue
}
// check encoded map
name := e["endpoint"]
desc := e["description"]
method := strings.Split(e["method"], ",")
path := strings.Split(e["path"], ",")
host := strings.Split(e["host"], ",")
handler := e["handler"]
if name != d.Name {
t.Fatalf("expected %v got %v", d.Name, name)
}
if desc != d.Description {
t.Fatalf("expected %v got %v", d.Description, desc)
}
if handler != d.Handler {
t.Fatalf("expected %v got %v", d.Handler, handler)
}
if ok := compare(d.Method, method); !ok {
t.Fatalf("expected %v got %v", d.Method, method)
}
if ok := compare(d.Path, path); !ok {
t.Fatalf("expected %v got %v", d.Path, path)
}
if ok := compare(d.Host, host); !ok {
t.Fatalf("expected %v got %v", d.Host, host)
}
if de.Name != d.Name {
t.Fatalf("expected %v got %v", d.Name, de.Name)
}
if de.Description != d.Description {
t.Fatalf("expected %v got %v", d.Description, de.Description)
}
if de.Handler != d.Handler {
t.Fatalf("expected %v got %v", d.Handler, de.Handler)
}
if ok := compare(d.Method, de.Method); !ok {
t.Fatalf("expected %v got %v", d.Method, de.Method)
}
if ok := compare(d.Path, de.Path); !ok {
t.Fatalf("expected %v got %v", d.Path, de.Path)
}
if ok := compare(d.Host, de.Host); !ok {
t.Fatalf("expected %v got %v", d.Host, de.Host)
}
}
}
func TestValidate(t *testing.T) {
epPcre := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"^/test/?$"},
}
if err := Validate(epPcre); err != nil {
t.Fatal(err)
}
epGpath := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/{id}"},
}
if err := Validate(epGpath); err != nil {
t.Fatal(err)
}
epPcreInvalid := &Endpoint{
Name: "Foo.Bar",
Description: "A test endpoint",
Handler: "meta",
Host: []string{"foo.com"},
Method: []string{"GET"},
Path: []string{"/test/?$"},
}
if err := Validate(epPcreInvalid); err == nil {
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
}
}

View File

@ -1,233 +0,0 @@
// Package client provides an api client
package client
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/gorilla/websocket"
"go-micro.dev/v5/logger"
)
const (
// local address for api.
localAddress = "http://localhost:8080"
)
// Options of the Client.
type Options struct {
// Token for authentication
Token string
// Address of the micro platform.
// By default it connects to live. Change it or use the local flag
// to connect to your local installation.
Address string
// Helper flag to help users connect to the default local address
Local bool
// set a timeout
Timeout time.Duration
}
// Request is the request of the generic `api-client` call.
type Request struct {
// eg. "go.micro.srv.greeter"
Service string `json:"service"`
// eg. "Say.Hello"
Endpoint string `json:"endpoint"`
// json and then base64 encoded body
Body string `json:"body"`
}
// Response is the response of the generic `api-client` call.
type Response struct {
// json and base64 encoded response body
Body string `json:"body"`
ID string `json:"id"`
Detail string `json:"detail"`
Status string `json:"status"`
// error fields. Error json example
// {"id":"go.micro.client","code":500,"detail":"malformed method name: \"\"","status":"Internal Server Error"}
Code int `json:"code"`
}
// Client enables generic calls to micro.
type Client struct {
options Options
}
// Stream is a websockets stream.
type Stream struct {
conn *websocket.Conn
service, endpoint string
}
// NewClient returns a generic micro client that connects to live by default.
func NewClient(options *Options) *Client {
ret := new(Client)
ret.options = Options{
Address: localAddress,
}
// no options provided
if options == nil {
return ret
}
if options.Token != "" {
ret.options.Token = options.Token
}
if options.Local {
ret.options.Address = localAddress
ret.options.Local = true
}
if options.Timeout > 0 {
ret.options.Timeout = options.Timeout
}
return ret
}
// SetToken sets the api auth token.
func (client *Client) SetToken(t string) {
client.options.Token = t
}
// SetTimeout sets the http client's timeout.
func (client *Client) SetTimeout(d time.Duration) {
client.options.Timeout = d
}
// Call enables you to access any endpoint of any service on Micro.
func (client *Client) Call(service, endpoint string, request, response interface{}) error {
// example curl: curl -XPOST -d '{"service": "go.micro.srv.greeter", "endpoint": "Say.Hello"}'
// -H 'Content-Type: application/json' http://localhost:8080/client {"body":"eyJtc2ciOiJIZWxsbyAifQ=="}
uri, err := url.Parse(client.options.Address)
if err != nil {
return err
}
// set the url to go through the v1 api
uri.Path = "/" + service + "/" + endpoint
b, err := marshalRequest(endpoint, request)
if err != nil {
return err
}
req, err := http.NewRequest("POST", uri.String(), bytes.NewBuffer(b))
if err != nil {
return err
}
// set the token if it exists
if len(client.options.Token) > 0 {
req.Header.Set("Authorization", "Bearer "+client.options.Token)
}
req.Header.Set("Content-Type", "application/json")
// if user didn't specify Timeout the default is 0 i.e no timeout
httpClient := &http.Client{
Timeout: client.options.Timeout,
}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer func() {
if err = resp.Body.Close(); err != nil {
logger.DefaultLogger.Log(logger.ErrorLevel, err)
}
}()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode <= 200 || resp.StatusCode > 300 {
return errors.New(string(body))
}
return unmarshalResponse(body, response)
}
// Stream enables the ability to stream via websockets.
func (client *Client) Stream(service, endpoint string, request interface{}) (*Stream, error) {
bytes, err := marshalRequest(endpoint, request)
if err != nil {
return nil, err
}
uri, err := url.Parse(client.options.Address)
if err != nil {
return nil, err
}
// set the url to go through the v1 api
uri.Path = "/" + service + "/" + endpoint
// replace http with websocket
uri.Scheme = strings.Replace(uri.Scheme, "http", "ws", 1)
// create the headers
header := make(http.Header)
// set the token if it exists
if len(client.options.Token) > 0 {
header.Set("Authorization", "Bearer "+client.options.Token)
}
header.Set("Content-Type", "application/json")
// dial the connection, connection not closed as conn is returned
conn, _, err := websocket.DefaultDialer.Dial(uri.String(), header)
if err != nil {
return nil, err
}
// send the first request
if err := conn.WriteMessage(websocket.TextMessage, bytes); err != nil {
return nil, err
}
return &Stream{conn, service, endpoint}, nil
}
// Recv will receive a message from a stream and unmarshal it.
func (s *Stream) Recv(v interface{}) error {
// read response
_, message, err := s.conn.ReadMessage()
if err != nil {
return err
}
return unmarshalResponse(message, v)
}
// Send will send a message into the stream.
func (s *Stream) Send(v interface{}) error {
b, err := marshalRequest(s.endpoint, v)
if err != nil {
return err
}
return s.conn.WriteMessage(websocket.TextMessage, b)
}
func marshalRequest(_ string, v interface{}) ([]byte, error) {
return json.Marshal(v)
}
func unmarshalResponse(body []byte, v interface{}) error {
return json.Unmarshal(body, &v)
}

View File

@ -1,24 +0,0 @@
package client
import (
"os"
"testing"
)
func TestBasicCall(t *testing.T) {
if v := os.Getenv("IN_TRAVIS_CI"); v == "yes" {
return
}
response := map[string]interface{}{}
if err := NewClient(&Options{
Token: os.Getenv("TOKEN"),
}).Call("groups", "list", map[string]interface{}{
"memberId": "random",
}, &response); err != nil {
t.Fatal(err)
}
if len(response) > 0 {
t.Fatal(len(response))
}
}

View File

@ -1,87 +0,0 @@
package api
import (
"context"
"go-micro.dev/v5/api/handler"
"go-micro.dev/v5/api/handler/rpc"
"go-micro.dev/v5/api/router/registry"
"go-micro.dev/v5/api/server"
"go-micro.dev/v5/api/server/http"
)
type api struct {
options Options
server server.Server
}
func newApi(opts ...Option) Api {
options := NewOptions(opts...)
rtr := options.Router
if rtr == nil {
// TODO: make configurable
rtr = registry.NewRouter()
}
// TODO: make configurable
hdlr := rpc.NewHandler(
handler.WithRouter(rtr),
)
// TODO: make configurable
// create a new server
srv := http.NewServer(options.Address)
// TODO: allow multiple handlers
// define the handler
srv.Handle("/", hdlr)
return &api{
options: options,
server: srv,
}
}
func (a *api) Init(opts ...Option) error {
for _, o := range opts {
o(&a.options)
}
return nil
}
// Get the options.
func (a *api) Options() Options {
return a.options
}
// Register a http handler.
func (a *api) Register(*Endpoint) error {
return nil
}
// Register a route.
func (a *api) Deregister(*Endpoint) error {
return nil
}
func (a *api) Run(ctx context.Context) error {
if err := a.server.Start(); err != nil {
return err
}
// wait to finish
<-ctx.Done()
if err := a.server.Stop(); err != nil {
return err
}
return nil
}
func (a *api) String() string {
return "http"
}

View File

@ -1,126 +0,0 @@
// Package api provides an http-rpc handler which provides the entire http request over rpc
package api
import (
"net/http"
"go-micro.dev/v5/api/handler"
api "go-micro.dev/v5/api/proto"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/client"
"go-micro.dev/v5/errors"
"go-micro.dev/v5/selector"
"go-micro.dev/v5/util/ctx"
)
type apiHandler struct {
opts handler.Options
}
const (
// Handler is the name of the Handler.
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(http.StatusInternalServerError)
w.Write([]byte(er.Error()))
return
}
var service *router.Route
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(http.StatusInternalServerError)
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(http.StatusInternalServerError)
w.Write([]byte(er.Error()))
return
}
// create request and response
c := a.opts.Client
req := c.NewRequest(service.Service, service.Endpoint.Name, request)
rsp := &api.Response{}
// create the context from headers
cx := ctx.FromRequest(r)
// create strategy:
so := selector.WithStrategy(strategy(service.Versions))
if err := c.Call(cx, req, rsp, client.WithSelectOption(so)); err != nil {
w.Header().Set("Content-Type", "application/json")
ce := errors.Parse(err.Error())
switch ce.Code {
case 0:
w.WriteHeader(http.StatusInternalServerError)
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"
}
// NewHandler returns an api.Handler.
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &apiHandler{
opts: options,
}
}

View File

@ -1,124 +0,0 @@
package api
import (
"fmt"
"mime"
"net"
"net/http"
"strings"
"github.com/oxtoacart/bpool"
api "go-micro.dev/v5/api/proto"
"go-micro.dev/v5/registry"
"go-micro.dev/v5/selector"
)
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: %w", 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
}
// strategy is a hack for selection.
func strategy(services []*registry.Service) selector.Strategy {
return func(_ []*registry.Service) selector.Next {
// ignore input to this function, use services above
return selector.Random(services)
}
}

View File

@ -1,46 +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])
}
}
}
}
}

View File

@ -1,147 +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"
"go-micro.dev/v5/api/handler"
proto "go-micro.dev/v5/api/proto"
"go-micro.dev/v5/util/ctx"
)
var (
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
type event struct {
opts handler.Options
}
var (
// Handler is the name of this handler.
Handler = "event"
versionRe = regexp.MustCompilePOSIX("^v[0-9]+$")
)
func eventName(parts []string) string {
return strings.Join(parts, ".")
}
func evRoute(namespace, myPath string) (string, string) {
myPath = path.Clean(myPath)
myPath = strings.TrimPrefix(myPath, "/")
if len(myPath) == 0 {
return namespace, Handler
}
parts := strings.Split(myPath, "/")
// no path
if len(parts) == 0 {
// topic: namespace
// action: event
return strings.Trim(namespace, "."), Handler
}
// Treat /v[0-9]+ as versioning
// /v1/foo/bar => topic: v1.foo action: bar
if len(parts) >= 2 && versionRe.MatchString(parts[0]) {
topic := namespace + "." + 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 := namespace + "." + strings.Join(parts[:1], ".")
action := eventName(parts[1:])
return topic, action
}
func (e *event) ServeHTTP(rsp http.ResponseWriter, req *http.Request) {
bsize := handler.DefaultMaxRecvSize
if e.opts.MaxRecvSize > 0 {
bsize = e.opts.MaxRecvSize
}
req.Body = http.MaxBytesReader(rsp, req.Body, bsize)
// request to topic:event
// create event
// publish to topic
topic, action := evRoute(e.opts.Namespace, req.URL.Path)
// create event
event := &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 req.Header {
header, ok := event.Header[key]
if !ok {
header = &proto.Pair{
Key: key,
}
event.Header[key] = header
}
header.Values = vals
}
// set body
if req.Method == http.MethodGet {
bytes, _ := json.Marshal(req.URL.Query())
event.Data = string(bytes)
} else {
// Read body
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(req.Body); err != nil {
http.Error(rsp, err.Error(), http.StatusInternalServerError)
return
}
event.Data = buf.String()
}
// get client
c := e.opts.Client
// create publication
p := c.NewMessage(topic, event)
// publish event
if err := c.Publish(ctx.FromRequest(req), p); err != nil {
http.Error(rsp, err.Error(), http.StatusInternalServerError)
return
}
}
func (e *event) String() string {
return Handler
}
// NewHandler returns a new event handler.
func NewHandler(opts ...handler.Option) handler.Handler {
return &event{
opts: handler.NewOptions(opts...),
}
}

View File

@ -1,14 +0,0 @@
// Package handler provides http handlers
package handler
import (
"net/http"
)
// Handler represents a HTTP handler that manages a request.
type Handler interface {
// standard http handler
http.Handler
// name of handler
String() string
}

View File

@ -1,86 +0,0 @@
// Package http is a http reverse proxy handler
package http
import (
"errors"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"go-micro.dev/v5/api/handler"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/selector"
)
const (
// Handler is the name of the handler.
Handler = "http"
)
type httpHandler struct {
options handler.Options
}
func (h *httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
service, err := h.getService(r)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
if len(service) == 0 {
w.WriteHeader(http.StatusNotFound)
return
}
rp, err := url.Parse(service)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
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 *router.Route
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")
}
// create a random selector
next := selector.Random(service.Versions)
// get the next node
s, err := next()
if err != nil {
return "", nil
}
return fmt.Sprintf("http://%s", s.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,
}
}

View File

@ -1,126 +0,0 @@
package http
import (
"net"
"net/http"
"net/http/httptest"
"testing"
"go-micro.dev/v5/api/handler"
"go-micro.dev/v5/api/resolver"
"go-micro.dev/v5/api/resolver/vpath"
"go-micro.dev/v5/api/router"
regRouter "go-micro.dev/v5/api/router/registry"
"go-micro.dev/v5/registry"
)
func testHttp(t *testing.T, path, service, ns string) {
r := registry.NewMemoryRegistry()
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
s := &registry.Service{
Name: service,
Nodes: []*registry.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)
}
// initialize the handler
rt := regRouter.NewRouter(
router.WithHandler("http"),
router.WithRegistry(r),
router.WithResolver(vpath.NewResolver(
resolver.WithNamespace(resolver.StaticNamespace(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)
})
}
}

View File

@ -1,84 +0,0 @@
package handler
import (
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/client"
"go-micro.dev/v5/logger"
)
var (
// DefaultMaxRecvSize is 10MiB.
DefaultMaxRecvSize int64 = 1024 * 1024 * 100
)
// Options is the list of api Options.
type Options struct {
Router router.Router
Client client.Client
Logger logger.Logger
Namespace string
MaxRecvSize int64
}
// Option is a api Option.
type Option func(o *Options)
// NewOptions fills in the blanks.
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
}
for _, o := range opts {
o(&options)
}
if options.Client == nil {
WithClient(client.DefaultClient)(&options)
}
if options.MaxRecvSize == 0 {
options.MaxRecvSize = DefaultMaxRecvSize
}
if options.Logger == nil {
options.Logger = logger.LoggerOrDefault(options.Logger)
}
return options
}
// WithNamespace specifies the namespace for the handler.
func WithNamespace(s string) Option {
return func(o *Options) {
o.Namespace = s
}
}
// WithRouter specifies a router to be used by the handler.
func WithRouter(r router.Router) Option {
return func(o *Options) {
o.Router = r
}
}
// WithClient sets the client for the handler.
func WithClient(c client.Client) Option {
return func(o *Options) {
o.Client = c
}
}
// WithMaxRecvSize specifies max body size.
func WithMaxRecvSize(size int64) Option {
return func(o *Options) {
o.MaxRecvSize = size
}
}
// WithLogger specifies the logger.
func WithLogger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}

View File

@ -1,564 +0,0 @@
// Package rpc is a go-micro rpc handler.
package rpc
import (
"encoding/json"
"io"
"net/http"
"net/textproto"
"strconv"
"strings"
jsonpatch "github.com/evanphx/json-patch/v5"
"github.com/oxtoacart/bpool"
"go-micro.dev/v5/api/handler"
"go-micro.dev/v5/api/internal/proto"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/client"
"go-micro.dev/v5/codec"
"go-micro.dev/v5/codec/jsonrpc"
"go-micro.dev/v5/codec/protorpc"
"go-micro.dev/v5/errors"
log "go-micro.dev/v5/logger"
"go-micro.dev/v5/metadata"
"go-micro.dev/v5/registry"
"go-micro.dev/v5/selector"
"go-micro.dev/v5/util/ctx"
"go-micro.dev/v5/util/qson"
)
const (
// Handler is the name of this handler.
Handler = "rpc"
packageID = "go.micro.api"
)
var (
// supported json codecs.
jsonCodecs = []string{
"application/grpc+json",
"application/json",
"application/json-rpc",
}
// support proto codecs.
protoCodecs = []string{
"application/grpc",
"application/grpc+proto",
"application/proto",
"application/protobuf",
"application/proto-rpc",
"application/octet-stream",
}
bufferPool = bpool.NewSizedBufferPool(1024, 8)
)
type rpcHandler struct {
opts handler.Options
}
type buffer struct {
io.ReadCloser
}
func (b *buffer) Write(_ []byte) (int, error) {
return 0, nil
}
// strategy is a hack for selection.
func strategy(services []*registry.Service) selector.Strategy {
return func(_ []*registry.Service) selector.Next {
// ignore input to this function, use services above
return selector.Random(services)
}
}
func (h *rpcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
logger := h.opts.Logger
bsize := handler.DefaultMaxRecvSize
if h.opts.MaxRecvSize > 0 {
bsize = h.opts.MaxRecvSize
}
r.Body = http.MaxBytesReader(w, r.Body, bsize)
defer r.Body.Close()
var service *router.Route
if h.opts.Router != nil {
// try get service from router
s, err := h.opts.Router.Route(r)
if err != nil {
werr := writeError(w, r, errors.InternalServerError(packageID, err.Error()))
if werr != nil {
logger.Log(log.ErrorLevel, werr)
}
return
}
service = s
} else {
// we have no way of routing the request
werr := writeError(w, r, errors.InternalServerError(packageID, "no route found"))
if werr != nil {
logger.Log(log.ErrorLevel, werr)
}
return
}
contentType := r.Header.Get("Content-Type")
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
if idx := strings.IndexRune(contentType, ';'); idx >= 0 {
contentType = contentType[:idx]
}
// micro client
myClient := h.opts.Client
// create context
myContext := ctx.FromRequest(r)
// get context from http handler wrappers
md, ok := metadata.FromContext(r.Context())
if !ok {
md = make(metadata.Metadata)
}
// fill contex with http headers
md["Host"] = r.Host
md["Method"] = r.Method
// get canonical headers
for k := range r.Header {
// may be need to get all values for key like r.Header.Values() provide in go 1.14
md[textproto.CanonicalMIMEHeaderKey(k)] = r.Header.Get(k)
}
// merge context with overwrite
myContext = metadata.MergeContext(myContext, md, true)
// set merged context to request
*r = *r.Clone(myContext)
// if stream we currently only support json
if isStream(r, service) {
// drop older context as it can have timeouts and create new
// md, _ := metadata.FromContext(cx)
// serveWebsocket(context.TODO(), w, r, service, c)
if err := serveWebsocket(myContext, w, r, service, myClient); err != nil {
logger.Log(log.ErrorLevel, err)
}
return
}
// create strategy
mySelector := selector.WithStrategy(strategy(service.Versions))
// walk the standard call path
// get payload
br, err := requestPayload(r)
if err != nil {
if werr := writeError(w, r, err); werr != nil {
logger.Log(log.ErrorLevel, werr)
}
return
}
var rsp []byte
switch {
// proto codecs
case hasCodec(contentType, protoCodecs):
request := &proto.Message{}
// if the extracted payload isn't empty lets use it
if len(br) > 0 {
request = proto.NewMessage(br)
}
// create request/response
response := &proto.Message{}
req := myClient.NewRequest(
service.Service,
service.Endpoint.Name,
request,
client.WithContentType(contentType),
)
// make the call
if err := myClient.Call(myContext, req, response, client.WithSelectOption(mySelector)); err != nil {
if werr := writeError(w, r, err); werr != nil {
logger.Log(log.ErrorLevel, werr)
}
return
}
// marshall response
rsp, err = response.Marshal()
if err != nil {
if werr := writeError(w, r, err); werr != nil {
logger.Log(log.ErrorLevel, werr)
}
return
}
default:
// if json codec is not present set to json
if !hasCodec(contentType, jsonCodecs) {
contentType = "application/json"
}
// default to trying json
var request json.RawMessage
// if the extracted payload isn't empty lets use it
if len(br) > 0 {
request = json.RawMessage(br)
}
// create request/response
var response json.RawMessage
req := myClient.NewRequest(
service.Service,
service.Endpoint.Name,
&request,
client.WithContentType(contentType),
)
// make the call
if err := myClient.Call(myContext, req, &response, client.WithSelectOption(mySelector)); err != nil {
if werr := writeError(w, r, err); werr != nil {
logger.Log(log.ErrorLevel, werr)
}
return
}
// marshall response
rsp, err = response.MarshalJSON()
if err != nil {
if werr := writeError(w, r, err); werr != nil {
logger.Log(log.ErrorLevel, werr)
}
return
}
}
// write the response
if err := writeResponse(w, r, rsp); err != nil {
logger.Log(log.ErrorLevel, err)
}
}
func (h *rpcHandler) String() string {
return Handler
}
func hasCodec(ct string, codecs []string) bool {
for _, codec := range codecs {
if ct == codec {
return true
}
}
return false
}
// requestPayload takes a *http.Request.
// If the request is a GET the query string parameters are extracted and marshaled to JSON and the raw bytes are returned.
// If the request method is a POST the request body is read and returned.
func requestPayload(r *http.Request) ([]byte, error) {
var err error
// we have to decode json-rpc and proto-rpc because we suck
// well actually because there's no proxy codec right now
myCt := r.Header.Get("Content-Type")
switch {
case strings.Contains(myCt, "application/json-rpc"):
msg := codec.Message{
Type: codec.Request,
Header: make(map[string]string),
}
c := jsonrpc.NewCodec(&buffer{r.Body})
if err = c.ReadHeader(&msg, codec.Request); err != nil {
return nil, err
}
var raw json.RawMessage
if err = c.ReadBody(&raw); err != nil {
return nil, err
}
return ([]byte)(raw), nil
case strings.Contains(myCt, "application/proto-rpc"), strings.Contains(myCt, "application/octet-stream"):
msg := codec.Message{
Type: codec.Request,
Header: make(map[string]string),
}
c := protorpc.NewCodec(&buffer{r.Body})
if err = c.ReadHeader(&msg, codec.Request); err != nil {
return nil, err
}
var raw proto.Message
if err = c.ReadBody(&raw); err != nil {
return nil, err
}
return raw.Marshal()
case strings.Contains(myCt, "application/www-x-form-urlencoded"), strings.Contains(myCt, "application/x-www-form-urlencoded"):
if err := r.ParseForm(); err != nil {
return nil, err
}
// generate a new set of values from the form
vals := make(map[string]string)
for k, v := range r.Form {
vals[k] = strings.Join(v, ",")
}
// marshal
return json.Marshal(vals)
// TODO: application/grpc
}
// otherwise as per usual
ctx := r.Context()
// dont user meadata.FromContext as it mangles names
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(map[string]string)
}
// allocate maximum
matches := make(map[string]interface{}, len(md))
bodydst := ""
// get fields from url path
for k, v := range md {
k = strings.ToLower(k)
// filter own keys
if strings.HasPrefix(k, "x-api-field-") {
matches[strings.TrimPrefix(k, "x-api-field-")] = v
delete(md, k)
} else if k == "x-api-body" {
bodydst = v
delete(md, k)
}
}
// map of all fields
req := make(map[string]interface{}, len(md))
// get fields from url values
if len(r.URL.RawQuery) > 0 {
umd := make(map[string]interface{})
err = qson.Unmarshal(&umd, r.URL.RawQuery)
if err != nil {
return nil, err
}
for k, v := range umd {
matches[k] = v
}
}
// restore context without fields
*r = *r.Clone(metadata.NewContext(ctx, md))
for k, v := range matches {
ps := strings.Split(k, ".")
if len(ps) == 1 {
req[k] = v
continue
}
em := make(map[string]interface{})
em[ps[len(ps)-1]] = v
for i := len(ps) - 2; i > 0; i-- {
nm := make(map[string]interface{})
nm[ps[i]] = em
em = nm
}
if vm, ok := req[ps[0]]; ok {
// nested map
nm := vm.(map[string]interface{})
for vk, vv := range em {
nm[vk] = vv
}
req[ps[0]] = nm
} else {
req[ps[0]] = em
}
}
pathbuf := []byte("{}")
if len(req) > 0 {
pathbuf, err = json.Marshal(req)
if err != nil {
return nil, err
}
}
urlbuf := []byte("{}")
out, err := jsonpatch.MergeMergePatches(urlbuf, pathbuf)
if err != nil {
return nil, err
}
switch r.Method {
case http.MethodGet:
// empty response
if strings.Contains(myCt, "application/json") && string(out) == "{}" {
return out, nil
} else if string(out) == "{}" && !strings.Contains(myCt, "application/json") {
return []byte{}, nil
}
return out, nil
case http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodDelete:
bodybuf := []byte("{}")
buf := bufferPool.Get()
defer bufferPool.Put(buf)
if _, err := buf.ReadFrom(r.Body); err != nil {
return nil, err
}
if b := buf.Bytes(); len(b) > 0 {
bodybuf = b
}
if bodydst == "" || bodydst == "*" {
if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
return out, nil
}
}
var jsonbody map[string]interface{}
if json.Valid(bodybuf) {
if err = json.Unmarshal(bodybuf, &jsonbody); err != nil {
return nil, err
}
}
dstmap := make(map[string]interface{})
ps := strings.Split(bodydst, ".")
if len(ps) == 1 {
if jsonbody != nil {
dstmap[ps[0]] = jsonbody
} else {
// old unexpected behavior
dstmap[ps[0]] = bodybuf
}
} else {
em := make(map[string]interface{})
if jsonbody != nil {
em[ps[len(ps)-1]] = jsonbody
} else {
// old unexpected behavior
em[ps[len(ps)-1]] = bodybuf
}
for i := len(ps) - 2; i > 0; i-- {
nm := make(map[string]interface{})
nm[ps[i]] = em
em = nm
}
dstmap[ps[0]] = em
}
bodyout, err := json.Marshal(dstmap)
if err != nil {
return nil, err
}
if out, err = jsonpatch.MergeMergePatches(out, bodyout); err == nil {
return out, nil
}
return bodybuf, nil
}
return []byte{}, nil
}
func writeError(rsp http.ResponseWriter, req *http.Request, err error) error {
ce := errors.Parse(err.Error())
switch ce.Code {
case 0:
// assuming it's totally screwed
ce.Code = http.StatusInternalServerError
ce.Id = packageID
ce.Status = http.StatusText(http.StatusInternalServerError)
ce.Detail = "error during request: " + ce.Detail
rsp.WriteHeader(http.StatusInternalServerError)
default:
rsp.WriteHeader(int(ce.Code))
}
// response content type
rsp.Header().Set("Content-Type", "application/json")
// Set trailers
if strings.Contains(req.Header.Get("Content-Type"), "application/grpc") {
rsp.Header().Set("Trailer", "grpc-status")
rsp.Header().Set("Trailer", "grpc-message")
rsp.Header().Set("grpc-status", "13")
rsp.Header().Set("grpc-message", ce.Detail)
}
_, werr := rsp.Write([]byte(ce.Error()))
return werr
}
func writeResponse(w http.ResponseWriter, r *http.Request, rsp []byte) error {
w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
w.Header().Set("Content-Length", strconv.Itoa(len(rsp)))
// Set trailers
if strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
w.Header().Set("Trailer", "grpc-status")
w.Header().Set("Trailer", "grpc-message")
w.Header().Set("grpc-status", "0")
w.Header().Set("grpc-message", "")
}
// write 204 status if rsp is nil
if len(rsp) == 0 {
w.WriteHeader(http.StatusNoContent)
}
// write response
_, err := w.Write(rsp)
return err
}
// NewHandler returns a new RPC handler.
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.NewOptions(opts...)
return &rpcHandler{
opts: options,
}
}

View File

@ -1,119 +0,0 @@
package rpc
import (
"bytes"
"encoding/json"
"net/http"
"reflect"
"testing"
"github.com/golang/protobuf/proto"
go_api "go-micro.dev/v5/api/proto"
)
func TestRequestPayloadFromRequest(t *testing.T) {
// our test event so that we can validate serializing / deserializing of true protos works
protoEvent := go_api.Event{
Name: "Test",
}
protoBytes, err := proto.Marshal(&protoEvent)
if err != nil {
t.Fatal("Failed to marshal proto", err)
}
jsonBytes, err := json.Marshal(protoEvent)
if err != nil {
t.Fatal("Failed to marshal proto to JSON ", err)
}
type jsonUrl struct {
Key1 string `json:"key1"`
Key2 string `json:"key2"`
Name string `json:"name"`
}
jUrl := &jsonUrl{Key1: "val1", Key2: "val2", Name: "Test"}
t.Run("extracting a json from a POST request with url params", func(t *testing.T) {
r, err := http.NewRequest("POST", "http://localhost/my/path?key1=val1&key2=val2", bytes.NewReader(jsonBytes))
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
extByte, err := requestPayload(r)
if err != nil {
t.Fatalf("Failed to extract payload from request: %v", err)
}
extJUrl := &jsonUrl{}
if err := json.Unmarshal(extByte, extJUrl); err != nil {
t.Fatalf("Failed to unmarshal payload from request: %v", err)
}
if !reflect.DeepEqual(extJUrl, jUrl) {
t.Fatalf("Expected %v and %v to match", extJUrl, jUrl)
}
})
t.Run("extracting a proto from a POST request", func(t *testing.T) {
r, err := http.NewRequest("POST", "http://localhost/my/path", bytes.NewReader(protoBytes))
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
extByte, err := requestPayload(r)
if err != nil {
t.Fatalf("Failed to extract payload from request: %v", err)
}
if string(extByte) != string(protoBytes) {
t.Fatalf("Expected %v and %v to match", string(extByte), string(protoBytes))
}
})
t.Run("extracting JSON from a POST request", func(t *testing.T) {
r, err := http.NewRequest("POST", "http://localhost/my/path", bytes.NewReader(jsonBytes))
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
extByte, err := requestPayload(r)
if err != nil {
t.Fatalf("Failed to extract payload from request: %v", err)
}
if string(extByte) != string(jsonBytes) {
t.Fatalf("Expected %v and %v to match", string(extByte), string(jsonBytes))
}
})
t.Run("extracting params from a GET request", func(t *testing.T) {
r, err := http.NewRequest("GET", "http://localhost/my/path", nil)
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
q := r.URL.Query()
q.Add("name", "Test")
r.URL.RawQuery = q.Encode()
extByte, err := requestPayload(r)
if err != nil {
t.Fatalf("Failed to extract payload from request: %v", err)
}
if string(extByte) != string(jsonBytes) {
t.Fatalf("Expected %v and %v to match", string(extByte), string(jsonBytes))
}
})
t.Run("GET request with no params", func(t *testing.T) {
r, err := http.NewRequest("GET", "http://localhost/my/path", nil)
if err != nil {
t.Fatalf("Failed to created http.Request: %v", err)
}
extByte, err := requestPayload(r)
if err != nil {
t.Fatalf("Failed to extract payload from request: %v", err)
}
if string(extByte) != "" {
t.Fatalf("Expected %v and %v to match", string(extByte), "")
}
})
}

View File

@ -1,245 +0,0 @@
package rpc
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"strings"
"time"
"github.com/gobwas/httphead"
"github.com/gobwas/ws"
"github.com/gobwas/ws/wsutil"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/client"
raw "go-micro.dev/v5/codec/bytes"
"go-micro.dev/v5/selector"
)
// serveWebsocket will stream rpc back over websockets assuming json.
func serveWebsocket(ctx context.Context, w http.ResponseWriter, r *http.Request, service *router.Route, c client.Client) (err error) {
var opCode ws.OpCode
myCt := r.Header.Get("Content-Type")
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
if idx := strings.IndexRune(myCt, ';'); idx >= 0 {
myCt = myCt[:idx]
}
// check proto from request
switch myCt {
case "application/json":
opCode = ws.OpText
default:
opCode = ws.OpBinary
}
hdr := make(http.Header)
if proto, ok := r.Header["Sec-Websocket-Protocol"]; ok {
for _, p := range proto {
if p == "binary" {
hdr["Sec-WebSocket-Protocol"] = []string{"binary"}
opCode = ws.OpBinary
}
}
}
payload, err := requestPayload(r)
if err != nil {
return
}
upgrader := ws.HTTPUpgrader{Timeout: 5 * time.Second,
Protocol: func(proto string) bool {
if strings.Contains(proto, "binary") {
return true
}
// fallback to support all protocols now
return true
},
Extension: func(httphead.Option) bool {
// disable extensions for compatibility
return false
},
Header: hdr,
}
conn, uRw, _, err := upgrader.Upgrade(r, w)
if err != nil {
return
}
defer func() {
if cErr := conn.Close(); cErr != nil && err == nil {
err = cErr
}
}()
var request interface{}
if !bytes.Equal(payload, []byte(`{}`)) {
switch myCt {
case "application/json", "":
m := json.RawMessage(payload)
request = &m
default:
request = &raw.Frame{Data: payload}
}
}
// we always need to set content type for message
if myCt == "" {
myCt = "application/json"
}
req := c.NewRequest(
service.Service,
service.Endpoint.Name,
request,
client.WithContentType(myCt),
client.StreamingRequest(),
)
cCtx, cancel := context.WithCancel(ctx)
defer cancel()
so := selector.WithStrategy(strategy(service.Versions))
// create a new stream
stream, err := c.Stream(cCtx, req, client.WithSelectOption(so))
if err != nil {
return
}
if request != nil {
if err = stream.Send(request); err != nil {
return
}
}
go func() {
if wErr := writeLoop(uRw, stream); wErr != nil && err == nil {
err = wErr
}
}()
rsp := stream.Response()
// receive from stream and send to client
for {
select {
case <-ctx.Done():
return nil
case <-stream.Context().Done():
return nil
default:
// read backend response body
buf, err := rsp.Read()
if err != nil {
// wants to avoid import grpc/status.Status
if strings.Contains(err.Error(), "context canceled") {
return nil
}
return err
}
// write the response
if err = wsutil.WriteServerMessage(uRw, opCode, buf); err != nil {
return err
}
if err = uRw.Flush(); err != nil {
return err
}
}
}
}
// writeLoop.
func writeLoop(rw io.ReadWriter, stream client.Stream) error {
// close stream when done
defer stream.Close()
for {
select {
case <-stream.Context().Done():
return nil
default:
buf, op, err := wsutil.ReadClientData(rw)
if err != nil {
if wserr, ok := err.(wsutil.ClosedError); ok {
switch wserr.Code {
case ws.StatusGoingAway:
// this happens when user leave the page
return nil
case ws.StatusNormalClosure, ws.StatusNoStatusRcvd:
// this happens when user close ws connection, or we don't get any status
return nil
default:
return err
}
}
}
switch op {
default:
// not relevant
continue
case ws.OpText, ws.OpBinary:
break
}
// send to backend
// default to trying json
// if the extracted payload isn't empty lets use it
request := &raw.Frame{Data: buf}
if err := stream.Send(request); err != nil {
return err
}
}
}
}
func isStream(r *http.Request, srv *router.Route) bool {
// check if it's a web socket
if !isWebSocket(r) {
return false
}
// check if the endpoint supports streaming
for _, service := range srv.Versions {
for _, ep := range service.Endpoints {
// skip if it doesn't match the name
if ep.Name != srv.Endpoint.Name {
continue
}
// matched if the name
if v := ep.Metadata["stream"]; v == "true" {
return true
}
}
}
return false
}
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
}

View File

@ -1,169 +0,0 @@
// Package web contains the web handler including websocket support
package web
import (
"errors"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"go-micro.dev/v5/api/handler"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/selector"
)
const (
// Handler is the name of the handler.
Handler = "web"
)
type webHandler struct {
opts handler.Options
}
func (wh *webHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
service, err := wh.getService(r)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
if len(service) == 0 {
w.WriteHeader(http.StatusNotFound)
return
}
rp, err := url.Parse(service)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
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 *router.Route
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")
}
// create a random selector
next := selector.Random(service.Versions)
// get the next node
s, err := next()
if err != nil {
return "", nil
}
return fmt.Sprintf("http://%s", s.Address), nil
}
// serveWebSocket used to serve a web socket proxied connection.
func (wh *webHandler) serveWebSocket(host string, rsp http.ResponseWriter, r *http.Request) {
req := new(http.Request)
*req = *r
if len(host) == 0 {
http.Error(rsp, "invalid host", http.StatusInternalServerError)
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(rsp, err.Error(), http.StatusInternalServerError)
return
}
// hijack the connection
hj, ok := rsp.(http.Hijacker)
if !ok {
http.Error(rsp, "failed to connect", http.StatusInternalServerError)
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"
}
// NewHandler returns a new web handler.
func NewHandler(opts ...handler.Option) handler.Handler {
return &webHandler{
opts: handler.NewOptions(opts...),
}
}

View File

@ -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}
}

View File

@ -1,45 +0,0 @@
package api
import (
"go-micro.dev/v5/api/router"
registry2 "go-micro.dev/v5/api/router/registry"
"go-micro.dev/v5/client"
"go-micro.dev/v5/registry"
)
func NewOptions(opts ...Option) Options {
options := Options{
Address: ":8080",
}
for _, o := range opts {
o(&options)
}
return options
}
// WithAddress sets the address to listen
func WithAddress(addr string) Option {
return func(o *Options) error {
o.Address = addr
return nil
}
}
// WithRouter sets the router to use e.g static or registry.
func WithRouter(r router.Router) Option {
return func(o *Options) error {
o.Router = r
return nil
}
}
// WithRegistry sets the api's client and router to use registry.
func WithRegistry(r registry.Registry) Option {
return func(o *Options) error {
o.Client = client.NewClient(client.Registry(r))
o.Router = registry2.NewRouter(router.WithRegistry(r))
return nil
}
}

View File

@ -1,335 +0,0 @@
// Code generated by protoc-gen-go. 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
type Pair struct {
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
Values []string `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Pair) Reset() { *m = Pair{} }
func (m *Pair) String() string { return proto.CompactTextString(m) }
func (*Pair) ProtoMessage() {}
func (*Pair) Descriptor() ([]byte, []int) {
return fileDescriptor_2df576b66d12087a, []int{0}
}
func (m *Pair) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Pair.Unmarshal(m, b)
}
func (m *Pair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Pair.Marshal(b, m, deterministic)
}
func (m *Pair) XXX_Merge(src proto.Message) {
xxx_messageInfo_Pair.Merge(m, src)
}
func (m *Pair) XXX_Size() int {
return xxx_messageInfo_Pair.Size(m)
}
func (m *Pair) XXX_DiscardUnknown() {
xxx_messageInfo_Pair.DiscardUnknown(m)
}
var xxx_messageInfo_Pair proto.InternalMessageInfo
func (m *Pair) GetKey() string {
if m != nil {
return m.Key
}
return ""
}
func (m *Pair) GetValues() []string {
if m != nil {
return m.Values
}
return nil
}
// A HTTP request as RPC
// Forward by the api handler
type Request struct {
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"`
Url string `protobuf:"bytes,7,opt,name=url,proto3" json:"url,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_2df576b66d12087a, []int{1}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Request.Unmarshal(m, b)
}
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
}
func (m *Request) XXX_Merge(src proto.Message) {
xxx_messageInfo_Request.Merge(m, src)
}
func (m *Request) XXX_Size() int {
return xxx_messageInfo_Request.Size(m)
}
func (m *Request) XXX_DiscardUnknown() {
xxx_messageInfo_Request.DiscardUnknown(m)
}
var xxx_messageInfo_Request proto.InternalMessageInfo
func (m *Request) GetMethod() string {
if m != nil {
return m.Method
}
return ""
}
func (m *Request) GetPath() string {
if m != nil {
return m.Path
}
return ""
}
func (m *Request) GetHeader() map[string]*Pair {
if m != nil {
return m.Header
}
return nil
}
func (m *Request) GetGet() map[string]*Pair {
if m != nil {
return m.Get
}
return nil
}
func (m *Request) GetPost() map[string]*Pair {
if m != nil {
return m.Post
}
return nil
}
func (m *Request) GetBody() string {
if m != nil {
return m.Body
}
return ""
}
func (m *Request) GetUrl() string {
if m != nil {
return m.Url
}
return ""
}
// A HTTP response as RPC
// Expected response for the api handler
type Response struct {
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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_2df576b66d12087a, []int{2}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Response.Unmarshal(m, b)
}
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
}
func (m *Response) XXX_Merge(src proto.Message) {
xxx_messageInfo_Response.Merge(m, src)
}
func (m *Response) XXX_Size() int {
return xxx_messageInfo_Response.Size(m)
}
func (m *Response) XXX_DiscardUnknown() {
xxx_messageInfo_Response.DiscardUnknown(m)
}
var xxx_messageInfo_Response proto.InternalMessageInfo
func (m *Response) GetStatusCode() int32 {
if m != nil {
return m.StatusCode
}
return 0
}
func (m *Response) GetHeader() map[string]*Pair {
if m != nil {
return m.Header
}
return nil
}
func (m *Response) GetBody() string {
if m != nil {
return m.Body
}
return ""
}
// A HTTP event as RPC
// Forwarded by the event handler
type Event struct {
// 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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_2df576b66d12087a, []int{3}
}
func (m *Event) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Event.Unmarshal(m, b)
}
func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Event.Marshal(b, m, deterministic)
}
func (m *Event) XXX_Merge(src proto.Message) {
xxx_messageInfo_Event.Merge(m, src)
}
func (m *Event) XXX_Size() int {
return xxx_messageInfo_Event.Size(m)
}
func (m *Event) XXX_DiscardUnknown() {
xxx_messageInfo_Event.DiscardUnknown(m)
}
var xxx_messageInfo_Event proto.InternalMessageInfo
func (m *Event) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Event) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Event) GetTimestamp() int64 {
if m != nil {
return m.Timestamp
}
return 0
}
func (m *Event) GetHeader() map[string]*Pair {
if m != nil {
return m.Header
}
return nil
}
func (m *Event) GetData() string {
if m != nil {
return m.Data
}
return ""
}
func init() {
proto.RegisterType((*Pair)(nil), "go.api.Pair")
proto.RegisterType((*Request)(nil), "go.api.Request")
proto.RegisterMapType((map[string]*Pair)(nil), "go.api.Request.GetEntry")
proto.RegisterMapType((map[string]*Pair)(nil), "go.api.Request.HeaderEntry")
proto.RegisterMapType((map[string]*Pair)(nil), "go.api.Request.PostEntry")
proto.RegisterType((*Response)(nil), "go.api.Response")
proto.RegisterMapType((map[string]*Pair)(nil), "go.api.Response.HeaderEntry")
proto.RegisterType((*Event)(nil), "go.api.Event")
proto.RegisterMapType((map[string]*Pair)(nil), "go.api.Event.HeaderEntry")
}
func init() { proto.RegisterFile("api/proto/api.proto", fileDescriptor_2df576b66d12087a) }
var fileDescriptor_2df576b66d12087a = []byte{
// 393 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0xcd, 0xce, 0xd3, 0x30,
0x10, 0x54, 0xe2, 0x24, 0x6d, 0xb6, 0x08, 0x21, 0x23, 0x21, 0x53, 0x2a, 0x54, 0xe5, 0x54, 0x21,
0x91, 0x42, 0xcb, 0x01, 0x71, 0x85, 0xaa, 0x1c, 0x2b, 0xbf, 0x81, 0xab, 0x58, 0x6d, 0x44, 0x13,
0x9b, 0xd8, 0xa9, 0xd4, 0x87, 0xe3, 0xc0, 0x63, 0xf0, 0x36, 0xc8, 0x1b, 0xf7, 0xe7, 0xab, 0xfa,
0x5d, 0xbe, 0xaf, 0xb7, 0x89, 0x3d, 0x3b, 0x3b, 0x3b, 0xeb, 0xc0, 0x6b, 0xa1, 0xcb, 0xa9, 0x6e,
0x94, 0x55, 0x53, 0xa1, 0xcb, 0x1c, 0x11, 0x4d, 0x36, 0x2a, 0x17, 0xba, 0xcc, 0x3e, 0x41, 0xb4,
0x12, 0x65, 0x43, 0x5f, 0x01, 0xf9, 0x25, 0x0f, 0x2c, 0x18, 0x07, 0x93, 0x94, 0x3b, 0x48, 0xdf,
0x40, 0xb2, 0x17, 0xbb, 0x56, 0x1a, 0x16, 0x8e, 0xc9, 0x24, 0xe5, 0xfe, 0x2b, 0xfb, 0x4b, 0xa0,
0xc7, 0xe5, 0xef, 0x56, 0x1a, 0xeb, 0x38, 0x95, 0xb4, 0x5b, 0x55, 0xf8, 0x42, 0xff, 0x45, 0x29,
0x44, 0x5a, 0xd8, 0x2d, 0x0b, 0xf1, 0x14, 0x31, 0x9d, 0x43, 0xb2, 0x95, 0xa2, 0x90, 0x0d, 0x23,
0x63, 0x32, 0x19, 0xcc, 0xde, 0xe5, 0x9d, 0x85, 0xdc, 0x8b, 0xe5, 0x3f, 0xf1, 0x76, 0x51, 0xdb,
0xe6, 0xc0, 0x3d, 0x95, 0x7e, 0x00, 0xb2, 0x91, 0x96, 0x45, 0x58, 0xc1, 0xae, 0x2b, 0x96, 0xd2,
0x76, 0x74, 0x47, 0xa2, 0x1f, 0x21, 0xd2, 0xca, 0x58, 0x16, 0x23, 0xf9, 0xed, 0x35, 0x79, 0xa5,
0x8c, 0x67, 0x23, 0xcd, 0x79, 0x5c, 0xab, 0xe2, 0xc0, 0x92, 0xce, 0xa3, 0xc3, 0x2e, 0x85, 0xb6,
0xd9, 0xb1, 0x5e, 0x97, 0x42, 0xdb, 0xec, 0x86, 0x4b, 0x18, 0x5c, 0xf8, 0xba, 0x11, 0x53, 0x06,
0x31, 0x06, 0x83, 0xb3, 0x0e, 0x66, 0x2f, 0x8e, 0x6d, 0x5d, 0xaa, 0xbc, 0xbb, 0xfa, 0x16, 0x7e,
0x0d, 0x86, 0x3f, 0xa0, 0x7f, 0xb4, 0xfb, 0x0c, 0x95, 0x05, 0xa4, 0xa7, 0x39, 0x9e, 0x2e, 0x93,
0xfd, 0x09, 0xa0, 0xcf, 0xa5, 0xd1, 0xaa, 0x36, 0x92, 0xbe, 0x07, 0x30, 0x56, 0xd8, 0xd6, 0x7c,
0x57, 0x85, 0x44, 0xb5, 0x98, 0x5f, 0x9c, 0xd0, 0x2f, 0xa7, 0xc5, 0x85, 0x98, 0xec, 0xe8, 0x9c,
0x6c, 0xa7, 0x70, 0x73, 0x73, 0xc7, 0x78, 0xc9, 0x39, 0xde, 0xbb, 0x85, 0x99, 0xfd, 0x0b, 0x20,
0x5e, 0xec, 0x65, 0x8d, 0x5b, 0xac, 0x45, 0x25, 0xbd, 0x08, 0x62, 0xfa, 0x12, 0xc2, 0xb2, 0xf0,
0x6f, 0x2f, 0x2c, 0x0b, 0x3a, 0x82, 0xd4, 0x96, 0x95, 0x34, 0x56, 0x54, 0x1a, 0xfd, 0x10, 0x7e,
0x3e, 0xa0, 0x9f, 0x4f, 0xe3, 0x45, 0x0f, 0x1f, 0x0e, 0x36, 0x78, 0x6c, 0xb6, 0x42, 0x58, 0xc1,
0xe2, 0xae, 0xa9, 0xc3, 0x77, 0x9b, 0x6d, 0x9d, 0xe0, 0x0f, 0x3a, 0xff, 0x1f, 0x00, 0x00, 0xff,
0xff, 0xd4, 0x6d, 0x70, 0x51, 0xb7, 0x03, 0x00, 0x00,
}

View File

@ -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

View File

@ -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;
}

View File

@ -1,42 +0,0 @@
// Package grpc resolves a grpc service like /greeter.Say/Hello to greeter service
package grpc
import (
"errors"
"net/http"
"strings"
"go-micro.dev/v5/api/resolver"
)
// Resolver is the gRPC Resolver.
type Resolver struct{}
// Resolve resolves a http.Request to an grpc Endpoint.
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
// /foo.Bar/Service
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
// [foo.Bar, Service]
parts := strings.Split(req.URL.Path[1:], "/")
// [foo, Bar]
name := strings.Split(parts[0], ".")
// foo
return &resolver.Endpoint{
Name: strings.Join(name[:len(name)-1], "."),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
// String returns the name of the resolver.
func (r *Resolver) String() string {
return "grpc"
}
// NewResolver creates a new gRPC resolver.
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{}
}

View File

@ -1,33 +0,0 @@
// Package host resolves using http host
package host
import (
"net/http"
"go-micro.dev/v5/api/resolver"
)
// Resolver is a host resolver.
type Resolver struct {
opts resolver.Options
}
// Resolve resolves a http.Request to an grpc Endpoint.
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
return &resolver.Endpoint{
Name: req.Host,
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
// String returns the name of the resolver.
func (r *Resolver) String() string {
return "host"
}
// NewResolver creates a new host resolver.
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}

View File

@ -1,33 +0,0 @@
package resolver
import (
"net/http"
)
// NewOptions wires options together.
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
if options.Namespace == nil {
options.Namespace = StaticNamespace("go.micro")
}
return options
}
// WithHandler sets the handler being used.
func WithHandler(h string) Option {
return func(o *Options) {
o.Handler = h
}
}
// WithNamespace sets the function which determines the namespace for a request.
func WithNamespace(n func(*http.Request) string) Option {
return func(o *Options) {
o.Namespace = n
}
}

View File

@ -1,40 +0,0 @@
// Package path resolves using http path
package path
import (
"net/http"
"strings"
"go-micro.dev/v5/api/resolver"
)
// Resolver is a path resolver.
type Resolver struct {
opts resolver.Options
}
// Resolve resolves a http.Request to an grpc Endpoint.
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, resolver.ErrNotFound
}
parts := strings.Split(req.URL.Path[1:], "/")
ns := r.opts.Namespace(req)
return &resolver.Endpoint{
Name: ns + "." + parts[0],
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
func (r *Resolver) String() string {
return "path"
}
// NewResolver returns a new path resolver.
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}

View File

@ -1,46 +0,0 @@
// Package resolver resolves a http request to an endpoint
package resolver
import (
"errors"
"net/http"
)
var (
ErrNotFound = errors.New("not found")
ErrInvalidPath = errors.New("invalid path")
)
// Resolver resolves requests to endpoints.
type Resolver interface {
Resolve(r *http.Request) (*Endpoint, error)
String() string
}
// Endpoint is the endpoint for a http request.
type Endpoint struct {
// e.g greeter
Name string
// HTTP Host e.g example.com
Host string
// HTTP Methods e.g GET, POST
Method string
// HTTP Path e.g /greeter.
Path string
}
// Options is a struct of available options.
type Options struct {
Namespace func(*http.Request) string
Handler string
}
// Option is a helper for a single option.
type Option func(o *Options)
// StaticNamespace returns the same namespace for each request.
func StaticNamespace(ns string) func(*http.Request) string {
return func(*http.Request) string {
return ns
}
}

View File

@ -1,72 +0,0 @@
// Package vpath resolves using http path and recognized versioned urls
package vpath
import (
"errors"
"net/http"
"regexp"
"strings"
"go-micro.dev/v5/api/resolver"
)
// NewResolver returns a new vpath resolver.
func NewResolver(opts ...resolver.Option) resolver.Resolver {
return &Resolver{opts: resolver.NewOptions(opts...)}
}
// Resolver is a vpath resolver.
type Resolver struct {
opts resolver.Options
}
var (
re = regexp.MustCompile("^v[0-9]+$")
)
// Resolve resolves a http.Request to an grpc Endpoint.
func (r *Resolver) Resolve(req *http.Request) (*resolver.Endpoint, error) {
if req.URL.Path == "/" {
return nil, errors.New("unknown name")
}
parts := strings.Split(req.URL.Path[1:], "/")
if len(parts) == 1 {
return &resolver.Endpoint{
Name: r.withNamespace(req, parts...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
// /v1/foo
if re.MatchString(parts[0]) {
return &resolver.Endpoint{
Name: r.withNamespace(req, parts[0:2]...),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
return &resolver.Endpoint{
Name: r.withNamespace(req, parts[0]),
Host: req.Host,
Method: req.Method,
Path: req.URL.Path,
}, nil
}
func (r *Resolver) String() string {
return "path"
}
func (r *Resolver) withNamespace(req *http.Request, parts ...string) string {
ns := r.opts.Namespace(req)
if len(ns) == 0 {
return strings.Join(parts, ".")
}
return strings.Join(append([]string{ns}, parts...), ".")
}

View File

@ -1,99 +0,0 @@
package router
import (
"errors"
"regexp"
"strings"
)
func strip(s string) string {
return strings.TrimSpace(s)
}
func slice(s string) []string {
var sl []string
for _, p := range strings.Split(s, ",") {
if str := strip(p); len(str) > 0 {
sl = append(sl, strip(p))
}
}
return sl
}
// Encode encodes an endpoint to endpoint metadata.
func Encode(e *Endpoint) map[string]string {
if e == nil {
return nil
}
// endpoint map
ep := make(map[string]string)
// set vals only if they exist
set := func(k, v string) {
if len(v) == 0 {
return
}
ep[k] = v
}
set("endpoint", e.Name)
set("description", e.Description)
set("handler", e.Handler)
set("method", strings.Join(e.Method, ","))
set("path", strings.Join(e.Path, ","))
set("host", strings.Join(e.Host, ","))
return ep
}
// Decode decodes endpoint metadata into an endpoint.
func Decode(e map[string]string) *Endpoint {
if e == nil {
return nil
}
return &Endpoint{
Name: e["endpoint"],
Description: e["description"],
Method: slice(e["method"]),
Path: slice(e["path"]),
Host: slice(e["host"]),
Handler: e["handler"],
}
}
// Validate validates an endpoint to guarantee it won't blow up when being served.
func Validate(e *Endpoint) error {
if e == nil {
return errors.New("endpoint is nil")
}
if len(e.Name) == 0 {
return errors.New("name required")
}
for _, p := range e.Path {
ps := p[0]
pe := p[len(p)-1]
if ps == '^' && pe == '$' {
_, err := regexp.CompilePOSIX(p)
if err != nil {
return err
}
} else if ps == '^' && pe != '$' {
return errors.New("invalid path")
} else if ps != '^' && pe == '$' {
return errors.New("invalid path")
}
}
if len(e.Handler) == 0 {
return errors.New("invalid handler")
}
return nil
}

View File

@ -1,65 +0,0 @@
package router
import (
"go-micro.dev/v5/api/resolver"
"go-micro.dev/v5/api/resolver/vpath"
"go-micro.dev/v5/logger"
"go-micro.dev/v5/registry"
)
// Options is a struct of options available.
type Options struct {
Registry registry.Registry
Resolver resolver.Resolver
Logger logger.Logger
Handler string
}
// Option is a helper for a single options.
type Option func(o *Options)
// NewOptions wires options together.
func NewOptions(opts ...Option) Options {
options := Options{
Handler: "meta",
Registry: registry.DefaultRegistry,
Logger: logger.DefaultLogger,
}
for _, o := range opts {
o(&options)
}
if options.Resolver == nil {
options.Resolver = vpath.NewResolver(
resolver.WithHandler(options.Handler),
)
}
return options
}
func WithHandler(h string) Option {
return func(o *Options) {
o.Handler = h
}
}
func WithRegistry(r registry.Registry) Option {
return func(o *Options) {
o.Registry = r
}
}
func WithResolver(r resolver.Resolver) Option {
return func(o *Options) {
o.Resolver = r
}
}
// WithLogger sets the underline logger.
func WithLogger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}

View File

@ -1,514 +0,0 @@
// Package registry provides a dynamic api service router
package registry
import (
"errors"
"fmt"
"net/http"
"regexp"
"strings"
"sync"
"time"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/api/router/util"
log "go-micro.dev/v5/logger"
"go-micro.dev/v5/metadata"
"go-micro.dev/v5/registry"
"go-micro.dev/v5/registry/cache"
)
// endpoint struct, that holds compiled pcre.
type endpoint struct {
hostregs []*regexp.Regexp
pathregs []util.Pattern
pcreregs []*regexp.Regexp
}
// router is the default router.
type registryRouter struct {
opts router.Options
// registry cache
rc cache.Cache
exit chan bool
eps map[string]*router.Route
// compiled regexp for host and path
ceps map[string]*endpoint
sync.RWMutex
}
func (r *registryRouter) isStopped() bool {
select {
case <-r.exit:
return true
default:
return false
}
}
// refresh list of api services.
func (r *registryRouter) refresh() {
var attempts int
logger := r.Options().Logger
for {
services, err := r.opts.Registry.ListServices()
if err != nil {
attempts++
logger.Logf(log.ErrorLevel, "unable to list services: %v", err)
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
attempts = 0
// for each service, get service and store endpoints
for _, s := range services {
service, err := r.rc.GetService(s.Name)
if err != nil {
logger.Logf(log.ErrorLevel, "unable to get service: %v", err)
continue
}
r.store(service)
}
// refresh list in 10 minutes... cruft
// use registry watching
select {
case <-time.After(time.Minute * 10):
case <-r.exit:
return
}
}
}
// process watch event.
func (r *registryRouter) process(res *registry.Result) {
logger := r.Options().Logger
// skip these things
if res == nil || res.Service == nil {
return
}
// get entry from cache
service, err := r.rc.GetService(res.Service.Name)
if err != nil {
logger.Logf(log.ErrorLevel, "unable to get service: %v", err)
return
}
// update our local endpoints
r.store(service)
}
// store local endpoint cache.
func (r *registryRouter) store(services []*registry.Service) {
logger := r.Options().Logger
// endpoints
eps := map[string]*router.Route{}
// services
names := map[string]bool{}
// create a new endpoint mapping
for _, service := range services {
// set names we need later
names[service.Name] = true
// map per endpoint
for _, sep := range service.Endpoints {
// create a key service:endpoint_name
key := fmt.Sprintf("%s.%s", service.Name, sep.Name)
// decode endpoint
end := router.Decode(sep.Metadata)
// if we got nothing skip
if err := router.Validate(end); err != nil {
logger.Logf(log.TraceLevel, "endpoint validation failed: %v", err)
continue
}
// try get endpoint
ep, ok := eps[key]
if !ok {
ep = &router.Route{Service: service.Name}
}
// overwrite the endpoint
ep.Endpoint = end
// append services
ep.Versions = append(ep.Versions, service)
// store it
eps[key] = ep
}
}
r.Lock()
defer r.Unlock()
// delete any existing eps for services we know
for key, route := range r.eps {
// skip what we don't care about
if !names[route.Service] {
continue
}
// ok we know this thing
// delete delete delete
delete(r.eps, key)
}
// now set the eps we have
for name, ep := range eps {
r.eps[name] = ep
cep := &endpoint{}
for _, h := range ep.Endpoint.Host {
if h == "" || h == "*" {
continue
}
hostreg, err := regexp.CompilePOSIX(h)
if err != nil {
logger.Logf(log.TraceLevel, "endpoint have invalid host regexp: %v", err)
continue
}
cep.hostregs = append(cep.hostregs, hostreg)
}
for _, p := range ep.Endpoint.Path {
var pcreok bool
if p[0] == '^' && p[len(p)-1] == '$' {
pcrereg, err := regexp.CompilePOSIX(p)
if err == nil {
cep.pcreregs = append(cep.pcreregs, pcrereg)
pcreok = true
}
}
rule, err := util.Parse(p)
if err != nil && !pcreok {
logger.Logf(log.TraceLevel, "endpoint have invalid path pattern: %v", err)
continue
} else if err != nil && pcreok {
continue
}
tpl := rule.Compile()
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "", util.PatternLogger(logger))
if err != nil {
logger.Logf(log.TraceLevel, "endpoint have invalid path pattern: %v", err)
continue
}
cep.pathregs = append(cep.pathregs, pathreg)
}
r.ceps[name] = cep
}
}
// watch for endpoint changes.
func (r *registryRouter) watch() {
var attempts int
logger := r.Options().Logger
for {
if r.isStopped() {
return
}
// watch for changes
w, err := r.opts.Registry.Watch()
if err != nil {
attempts++
logger.Logf(log.ErrorLevel, "error watching endpoints: %v", err)
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
ch := make(chan bool)
go func() {
select {
case <-ch:
w.Stop()
case <-r.exit:
w.Stop()
}
}()
// reset if we get here
attempts = 0
for {
// process next event
res, err := w.Next()
if err != nil {
logger.Logf(log.ErrorLevel, "error getting next endoint: %v", err)
close(ch)
break
}
r.process(res)
}
}
}
func (r *registryRouter) Options() router.Options {
return r.opts
}
func (r *registryRouter) Stop() error {
select {
case <-r.exit:
return nil
default:
close(r.exit)
r.rc.Stop()
}
return nil
}
func (r *registryRouter) Register(ep *router.Route) error {
return nil
}
func (r *registryRouter) Deregister(ep *router.Route) error {
return nil
}
func (r *registryRouter) Endpoint(req *http.Request) (*router.Route, error) {
logger := r.Options().Logger
if r.isStopped() {
return nil, errors.New("router closed")
}
r.RLock()
defer r.RUnlock()
var idx int
if len(req.URL.Path) > 0 && req.URL.Path != "/" {
idx = 1
}
path := strings.Split(req.URL.Path[idx:], "/")
// use the first match
// TODO: weighted matching
for n, endpoint := range r.eps {
cep, ok := r.ceps[n]
if !ok {
continue
}
ep := endpoint.Endpoint
var mMatch, hMatch, pMatch bool
// 1. try method
for _, m := range ep.Method {
if m == req.Method {
mMatch = true
break
}
}
if !mMatch {
continue
}
logger.Logf(log.DebugLevel, "api method match %s", req.Method)
// 2. try host
if len(ep.Host) == 0 {
hMatch = true
} else {
for idx, h := range ep.Host {
if h == "" || h == "*" {
hMatch = true
break
} else if cep.hostregs[idx].MatchString(req.URL.Host) {
hMatch = true
break
}
}
}
if !hMatch {
continue
}
logger.Logf(log.DebugLevel, "api host match %s", req.URL.Host)
// 3. try path via google.api path matching
for _, pathreg := range cep.pathregs {
matches, err := pathreg.Match(path, "")
if err != nil {
logger.Logf(log.DebugLevel, "api gpath not match %s != %v", path, pathreg)
continue
}
logger.Logf(log.DebugLevel, "api gpath match %s = %v", path, pathreg)
pMatch = true
ctx := req.Context()
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(metadata.Metadata)
}
for k, v := range matches {
md[fmt.Sprintf("x-api-field-%s", k)] = v
}
*req = *req.Clone(metadata.NewContext(ctx, md))
break
}
if !pMatch {
// 4. try path via pcre path matching
for _, pathreg := range cep.pcreregs {
if !pathreg.MatchString(req.URL.Path) {
logger.Logf(log.DebugLevel, "api pcre path not match %s != %v", path, pathreg)
continue
}
logger.Logf(log.DebugLevel, "api pcre path match %s != %v", path, pathreg)
pMatch = true
break
}
}
if !pMatch {
continue
}
// TODO: Percentage traffic
// we got here, so its a match
return endpoint, nil
}
// no match
return nil, errors.New("not found")
}
func (r *registryRouter) Route(req *http.Request) (*router.Route, error) {
if r.isStopped() {
return nil, errors.New("router closed")
}
// try get an endpoint
ep, err := r.Endpoint(req)
if err == nil {
return ep, nil
}
// error not nil
// ignore that shit
// TODO: don't ignore that shit
// get the service name
rsp, err := r.opts.Resolver.Resolve(req)
if err != nil {
return nil, err
}
// service name
name := rsp.Name
// get service
services, err := r.rc.GetService(name)
if err != nil {
return nil, err
}
// only use endpoint matching when the meta handler is set aka api.Default
switch r.opts.Handler {
// rpc handlers
case "meta", "api", "rpc":
handler := r.opts.Handler
// set default handler to api
if r.opts.Handler == "meta" {
handler = "rpc"
}
// extract endpoint from Path, case-sensitive
// just test it in this case, maybe should put the code somewhere else
ep_name := rsp.Method
comps := strings.Split(rsp.Path, "/")
switch len(comps) {
case 3:
ep_name = comps[1] + "." + comps[2]
case 4:
ep_name = comps[2] + "." + comps[3]
}
// construct api service
return &router.Route{
Service: name,
Endpoint: &router.Endpoint{
Name: ep_name,
Handler: handler,
},
Versions: services,
}, nil
// http handler
case "http", "proxy", "web":
// construct api service
return &router.Route{
Service: name,
Endpoint: &router.Endpoint{
Name: req.URL.String(),
Handler: r.opts.Handler,
Host: []string{req.Host},
Method: []string{req.Method},
Path: []string{req.URL.Path},
},
Versions: services,
}, nil
}
return nil, errors.New("unknown handler")
}
func newRouter(opts ...router.Option) *registryRouter {
options := router.NewOptions(opts...)
r := &registryRouter{
exit: make(chan bool),
opts: options,
rc: cache.New(options.Registry),
eps: make(map[string]*router.Route),
ceps: make(map[string]*endpoint),
}
go r.watch()
go r.refresh()
return r
}
// NewRouter returns the default router.
func NewRouter(opts ...router.Option) router.Router {
return newRouter(opts...)
}

View File

@ -1,34 +0,0 @@
package registry
import (
"testing"
"github.com/stretchr/testify/assert"
"go-micro.dev/v5/registry"
)
func TestStoreRegex(t *testing.T) {
router := newRouter()
router.store([]*registry.Service{
{
Name: "Foobar",
Version: "latest",
Endpoints: []*registry.Endpoint{
{
Name: "foo",
Metadata: map[string]string{
"endpoint": "FooEndpoint",
"description": "Some description",
"method": "POST",
"path": "^/foo/$",
"handler": "rpc",
},
},
},
Metadata: map[string]string{},
},
},
)
assert.Len(t, router.ceps["Foobar.foo"].pcreregs, 1)
}

View File

@ -1,49 +0,0 @@
// Package router provides api service routing
package router
import (
"net/http"
"go-micro.dev/v5/registry"
)
// Router is used to determine an endpoint for a request.
type Router interface {
// Returns options
Options() Options
// Register endpoint in router
Register(r *Route) error
// Deregister endpoint from router
Deregister(r *Route) error
// Route returns an api.Service route
Route(r *http.Request) (*Route, error)
// Stop the router
Stop() error
}
type Route struct {
// Name of service
Service string
// The endpoint for this service
Endpoint *Endpoint
// Versions of this service
Versions []*registry.Service
}
// Endpoint is a mapping between an RPC method and HTTP endpoint.
type Endpoint struct {
// RPC Method e.g. Greeter.Hello
Name string
// What the endpoint is for
Description string
// API Handler e.g rpc, proxy
Handler string
// HTTP Host e.g example.com
Host []string
// HTTP Methods e.g GET, POST
Method []string
// HTTP Path e.g /greeter. Expect POSIX regex
Path []string
// Stream flag
Stream bool
}

View File

@ -1,369 +0,0 @@
package static
import (
"errors"
"fmt"
"net/http"
"regexp"
"strings"
"sync"
"go-micro.dev/v5/api/router"
"go-micro.dev/v5/api/router/util"
log "go-micro.dev/v5/logger"
"go-micro.dev/v5/metadata"
"go-micro.dev/v5/registry"
rutil "go-micro.dev/v5/util/registry"
)
type endpoint struct {
apiep *router.Endpoint
hostregs []*regexp.Regexp
pathregs []util.Pattern
pcreregs []*regexp.Regexp
}
// Router is the default router.
type Router struct {
opts router.Options
exit chan bool
eps map[string]*endpoint
sync.RWMutex
}
func (r *Router) isStopd() bool {
select {
case <-r.exit:
return true
default:
return false
}
}
/*
// watch for endpoint changes
func (r *staticRouter) watch() {
var attempts int
for {
if r.isStopd() {
return
}
// watch for changes
w, err := r.opts.Registry.Watch()
if err != nil {
attempts++
log.Println("Error watching endpoints", err)
time.Sleep(time.Duration(attempts) * time.Second)
continue
}
ch := make(chan bool)
go func() {
select {
case <-ch:
w.Stop()
case <-r.exit:
w.Stop()
}
}()
// reset if we get here
attempts = 0
for {
// process next event
res, err := w.Next()
if err != nil {
log.Println("Error getting next endpoint", err)
close(ch)
break
}
r.process(res)
}
}
}
*/
func (r *Router) Register(route *router.Route) error {
myEndpoint := route.Endpoint
if err := router.Validate(myEndpoint); err != nil {
return err
}
var (
pathregs []util.Pattern
hostregs []*regexp.Regexp
pcreregs []*regexp.Regexp
)
for _, h := range myEndpoint.Host {
if h == "" || h == "*" {
continue
}
hostreg, err := regexp.CompilePOSIX(h)
if err != nil {
return err
}
hostregs = append(hostregs, hostreg)
}
for _, p := range myEndpoint.Path {
var pcreok bool
// pcre only when we have start and end markers
if p[0] == '^' && p[len(p)-1] == '$' {
pcrereg, err := regexp.CompilePOSIX(p)
if err == nil {
pcreregs = append(pcreregs, pcrereg)
pcreok = true
}
}
rule, err := util.Parse(p)
if err != nil && !pcreok {
return err
} else if err != nil && pcreok {
continue
}
tpl := rule.Compile()
pathreg, err := util.NewPattern(tpl.Version, tpl.OpCodes, tpl.Pool, "", util.PatternLogger(r.Options().Logger))
if err != nil {
return err
}
pathregs = append(pathregs, pathreg)
}
r.Lock()
r.eps[myEndpoint.Name] = &endpoint{
apiep: myEndpoint,
pcreregs: pcreregs,
pathregs: pathregs,
hostregs: hostregs,
}
r.Unlock()
return nil
}
func (r *Router) Deregister(route *router.Route) error {
ep := route.Endpoint
if err := router.Validate(ep); err != nil {
return err
}
r.Lock()
delete(r.eps, ep.Name)
r.Unlock()
return nil
}
func (r *Router) Options() router.Options {
return r.opts
}
func (r *Router) Stop() error {
select {
case <-r.exit:
return nil
default:
close(r.exit)
}
return nil
}
func (r *Router) Endpoint(req *http.Request) (*router.Route, error) {
myEndpoint, err := r.endpoint(req)
if err != nil {
return nil, err
}
epf := strings.Split(myEndpoint.apiep.Name, ".")
services, err := r.opts.Registry.GetService(epf[0])
if err != nil {
return nil, err
}
// hack for stream endpoint
if myEndpoint.apiep.Stream {
svcs := rutil.Copy(services)
for _, svc := range svcs {
if len(svc.Endpoints) == 0 {
e := &registry.Endpoint{}
e.Name = strings.Join(epf[1:], ".")
e.Metadata = make(map[string]string)
e.Metadata["stream"] = "true"
svc.Endpoints = append(svc.Endpoints, e)
}
for _, e := range svc.Endpoints {
e.Name = strings.Join(epf[1:], ".")
e.Metadata = make(map[string]string)
e.Metadata["stream"] = "true"
}
}
services = svcs
}
svc := &router.Route{
Service: epf[0],
Endpoint: &router.Endpoint{
Name: strings.Join(epf[1:], "."),
Handler: "rpc",
Host: myEndpoint.apiep.Host,
Method: myEndpoint.apiep.Method,
Path: myEndpoint.apiep.Path,
Stream: myEndpoint.apiep.Stream,
},
Versions: services,
}
return svc, nil
}
func (r *Router) endpoint(req *http.Request) (*endpoint, error) {
logger := r.Options().Logger
if r.isStopd() {
return nil, errors.New("router closed")
}
r.RLock()
defer r.RUnlock()
var idx int
if len(req.URL.Path) > 0 && req.URL.Path != "/" {
idx = 1
}
path := strings.Split(req.URL.Path[idx:], "/")
// use the first match
// TODO: weighted matching
for _, myEndpoint := range r.eps {
var mMatch, hMatch, pMatch bool
// 1. try method
for _, m := range myEndpoint.apiep.Method {
if m == req.Method {
mMatch = true
break
}
}
if !mMatch {
continue
}
logger.Logf(log.DebugLevel, "api method match %s", req.Method)
// 2. try host
if len(myEndpoint.apiep.Host) == 0 {
hMatch = true
} else {
for idx, h := range myEndpoint.apiep.Host {
if h == "" || h == "*" {
hMatch = true
break
} else if myEndpoint.hostregs[idx].MatchString(req.URL.Host) {
hMatch = true
break
}
}
}
if !hMatch {
continue
}
logger.Logf(log.DebugLevel, "api host match %s", req.URL.Host)
// 3. try google.api path
for _, pathreg := range myEndpoint.pathregs {
matches, err := pathreg.Match(path, "")
if err != nil {
logger.Logf(log.DebugLevel, "api gpath not match %s != %v", path, pathreg)
continue
}
logger.Logf(log.DebugLevel, "api gpath match %s = %v", path, pathreg)
pMatch = true
ctx := req.Context()
md, ok := metadata.FromContext(ctx)
if !ok {
md = make(metadata.Metadata)
}
for k, v := range matches {
md[fmt.Sprintf("x-api-field-%s", k)] = v
}
*req = *req.Clone(metadata.NewContext(ctx, md))
break
}
if !pMatch {
// 4. try path via pcre path matching
for _, pathreg := range myEndpoint.pcreregs {
if !pathreg.MatchString(req.URL.Path) {
logger.Logf(log.DebugLevel, "api pcre path not match %s != %v", req.URL.Path, pathreg)
continue
}
pMatch = true
break
}
}
if !pMatch {
continue
}
// TODO: Percentage traffic
// we got here, so its a match
return myEndpoint, nil
}
// no match
return nil, fmt.Errorf("endpoint not found for %v", req.URL)
}
func (r *Router) Route(req *http.Request) (*router.Route, error) {
if r.isStopd() {
return nil, errors.New("router closed")
}
// try get an endpoint
ep, err := r.Endpoint(req)
if err != nil {
return nil, err
}
return ep, nil
}
// NewRouter returns a new static router.
func NewRouter(opts ...router.Option) *Router {
options := router.NewOptions(opts...)
r := &Router{
exit: make(chan bool),
opts: options,
eps: make(map[string]*endpoint),
}
// go r.watch()
return r
}

View File

@ -1,27 +0,0 @@
Copyright (c) 2015, Gengo, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Gengo, Inc. nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,122 +0,0 @@
package util
// download from
// https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile.go
const (
opcodeVersion = 1
)
// Template is a compiled representation of path templates.
type Template struct {
// Verb is a VERB part in the template.
Verb string
// Original template (example: /v1/a_bit_of_everything)
Template string
// OpCodes is a sequence of operations.
OpCodes []int
// Pool is a constant pool
Pool []string
// Fields is a list of field paths bound in this template.
Fields []string
// Version is the version number of the format.
Version int
}
// Compiler compiles utilities representation of path templates into marshallable operations.
// They can be unmarshalled by runtime.NewPattern.
type Compiler interface {
Compile() Template
}
type op struct {
// str is a string operand of the code.
// operand is ignored if str is not empty.
str string
// code is the opcode of the operation
code OpCode
// operand is a numeric operand of the code.
operand int
}
func (w wildcard) compile() []op {
return []op{
{code: OpPush},
}
}
func (w deepWildcard) compile() []op {
return []op{
{code: OpPushM},
}
}
func (l literal) compile() []op {
return []op{
{
code: OpLitPush,
str: string(l),
},
}
}
func (v variable) compile() []op {
var ops []op
for _, s := range v.segments {
ops = append(ops, s.compile()...)
}
ops = append(ops, op{
code: OpConcatN,
operand: len(v.segments),
}, op{
code: OpCapture,
str: v.path,
})
return ops
}
func (t template) Compile() Template {
var rawOps []op
for _, s := range t.segments {
rawOps = append(rawOps, s.compile()...)
}
var (
ops []int
pool []string
fields []string
)
consts := make(map[string]int)
for _, op := range rawOps {
ops = append(ops, int(op.code))
if op.str == "" {
ops = append(ops, op.operand)
} else {
if _, ok := consts[op.str]; !ok {
consts[op.str] = len(pool)
pool = append(pool, op.str)
}
ops = append(ops, consts[op.str])
}
if op.code == OpCapture {
fields = append(fields, op.str)
}
}
return Template{
Version: opcodeVersion,
OpCodes: ops,
Pool: pool,
Verb: t.verb,
Fields: fields,
Template: t.template,
}
}

View File

@ -1,122 +0,0 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/compile_test.go
import (
"reflect"
"testing"
)
const (
operandFiller = 0
)
func TestCompile(t *testing.T) {
for _, spec := range []struct {
segs []segment
verb string
ops []int
pool []string
fields []string
}{
{},
{
segs: []segment{
wildcard{},
},
ops: []int{int(OpPush), operandFiller},
},
{
segs: []segment{
deepWildcard{},
},
ops: []int{int(OpPushM), operandFiller},
},
{
segs: []segment{
literal("v1"),
},
ops: []int{int(OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
literal("v1"),
},
verb: "LOCK",
ops: []int{int(OpLitPush), 0},
pool: []string{"v1"},
},
{
segs: []segment{
variable{
path: "name.nested",
segments: []segment{
wildcard{},
},
},
},
ops: []int{
int(OpPush), operandFiller,
int(OpConcatN), 1,
int(OpCapture), 0,
},
pool: []string{"name.nested"},
fields: []string{"name.nested"},
},
{
segs: []segment{
literal("obj"),
variable{
path: "name.nested",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
variable{
path: "obj",
segments: []segment{
deepWildcard{},
},
},
},
ops: []int{
int(OpLitPush), 0,
int(OpLitPush), 1,
int(OpPush), operandFiller,
int(OpLitPush), 2,
int(OpConcatN), 3,
int(OpCapture), 3,
int(OpPushM), operandFiller,
int(OpConcatN), 1,
int(OpCapture), 0,
},
pool: []string{"obj", "a", "b", "name.nested"},
fields: []string{"name.nested", "obj"},
},
} {
tmpl := template{
segments: spec.segs,
verb: spec.verb,
}
compiled := tmpl.Compile()
if got, want := compiled.Version, opcodeVersion; got != want {
t.Errorf("tmpl.Compile().Version = %d; want %d; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.OpCodes, spec.ops; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().OpCodes = %v; want %v; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Pool, spec.pool; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Pool = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Verb, spec.verb; got != want {
t.Errorf("tmpl.Compile().Verb = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
if got, want := compiled.Fields, spec.fields; !reflect.DeepEqual(got, want) {
t.Errorf("tmpl.Compile().Fields = %q; want %q; segs=%#v, verb=%q", got, want, spec.segs, spec.verb)
}
}
}

View File

@ -1,396 +0,0 @@
package util
// download from
// https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse.go
import (
"fmt"
"strings"
log "go-micro.dev/v5/logger"
)
// InvalidTemplateError indicates that the path template is not valid.
type InvalidTemplateError struct {
tmpl string
msg string
}
func (e InvalidTemplateError) Error() string {
return fmt.Sprintf("%s: %s", e.msg, e.tmpl)
}
// Parse parses the string representation of path template.
func Parse(tmpl string) (Compiler, error) {
if !strings.HasPrefix(tmpl, "/") {
return template{}, InvalidTemplateError{tmpl: tmpl, msg: "no leading /"}
}
tokens, verb := tokenize(tmpl[1:])
p := parser{tokens: tokens}
segs, err := p.topLevelSegments()
if err != nil {
return template{}, InvalidTemplateError{tmpl: tmpl, msg: err.Error()}
}
return template{
segments: segs,
verb: verb,
template: tmpl,
}, nil
}
func tokenize(path string) (tokens []string, verb string) {
if path == "" {
return []string{eof}, ""
}
const (
init = iota
field
nested
)
var (
st = init
)
for path != "" {
var idx int
switch st {
case init:
idx = strings.IndexAny(path, "/{")
case field:
idx = strings.IndexAny(path, ".=}")
case nested:
idx = strings.IndexAny(path, "/}")
}
if idx < 0 {
tokens = append(tokens, path)
break
}
switch r := path[idx]; r {
case '/', '.':
case '{':
st = field
case '=':
st = nested
case '}':
st = init
}
if idx == 0 {
tokens = append(tokens, path[idx:idx+1])
} else {
tokens = append(tokens, path[:idx], path[idx:idx+1])
}
path = path[idx+1:]
}
l := len(tokens)
t := tokens[l-1]
if idx := strings.LastIndex(t, ":"); idx == 0 {
tokens, verb = tokens[:l-1], t[1:]
} else if idx > 0 {
tokens[l-1], verb = t[:idx], t[idx+1:]
}
tokens = append(tokens, eof)
return tokens, verb
}
// parser is a parser of the template syntax defined in github.com/googleapis/googleapis/google/api/http.proto.
type parser struct {
logger log.Logger
tokens []string
accepted []string
}
// topLevelSegments is the target of this parser.
func (p *parser) topLevelSegments() ([]segment, error) {
logger := log.LoggerOrDefault(p.logger)
logger.Logf(log.DebugLevel, "Parsing %q", p.tokens)
segs, err := p.segments()
if err != nil {
return nil, err
}
logger.Logf(log.DebugLevel, "accept segments: %q; %q", p.accepted, p.tokens)
if _, err := p.accept(typeEOF); err != nil {
return nil, fmt.Errorf("unexpected token %q after segments %q", p.tokens[0], strings.Join(p.accepted, ""))
}
logger.Logf(log.DebugLevel, "accept eof: %q; %q", p.accepted, p.tokens)
return segs, nil
}
func (p *parser) segments() ([]segment, error) {
logger := log.LoggerOrDefault(p.logger)
s, err := p.segment()
if err != nil {
return nil, err
}
logger.Logf(log.DebugLevel, "accept segment: %q; %q", p.accepted, p.tokens)
segs := []segment{s}
for {
if _, err := p.accept("/"); err != nil {
return segs, nil
}
s, err := p.segment()
if err != nil {
return segs, err
}
segs = append(segs, s)
logger.Logf(log.DebugLevel, "accept segment: %q; %q", p.accepted, p.tokens)
}
}
func (p *parser) segment() (segment, error) {
if _, err := p.accept("*"); err == nil {
return wildcard{}, nil
}
if _, err := p.accept("**"); err == nil {
return deepWildcard{}, nil
}
if l, err := p.literal(); err == nil {
return l, nil
}
v, err := p.variable()
if err != nil {
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %w", err)
}
return v, err
}
func (p *parser) literal() (segment, error) {
lit, err := p.accept(typeLiteral)
if err != nil {
return nil, err
}
return literal(lit), nil
}
func (p *parser) variable() (segment, error) {
if _, err := p.accept("{"); err != nil {
return nil, err
}
path, err := p.fieldPath()
if err != nil {
return nil, err
}
var segs []segment
if _, err := p.accept("="); err == nil {
segs, err = p.segments()
if err != nil {
return nil, fmt.Errorf("invalid segment in variable %q: %w", path, err)
}
} else {
segs = []segment{wildcard{}}
}
if _, err := p.accept("}"); err != nil {
return nil, fmt.Errorf("unterminated variable segment: %s", path)
}
return variable{
path: path,
segments: segs,
}, nil
}
func (p *parser) fieldPath() (string, error) {
c, err := p.accept(typeIdent)
if err != nil {
return "", err
}
components := []string{c}
for {
if _, err = p.accept("."); err != nil {
return strings.Join(components, "."), nil
}
c, err := p.accept(typeIdent)
if err != nil {
return "", fmt.Errorf("invalid field path component: %v", err)
}
components = append(components, c)
}
}
// A termType is a type of terminal symbols.
type termType string
// These constants define some of valid values of termType.
// They improve readability of parse functions.
//
// You can also use "/", "*", "**", "." or "=" as valid values.
const (
typeIdent = termType("ident")
typeLiteral = termType("literal")
typeEOF = termType("$")
)
const (
// eof is the terminal symbol which always appears at the end of token sequence.
eof = "\u0000"
)
// accept tries to accept a token in "p".
// This function consumes a token and returns it if it matches to the specified "term".
// If it doesn't match, the function does not consume any tokens and return an error.
func (p *parser) accept(term termType) (string, error) {
t := p.tokens[0]
switch term {
case "/", "*", "**", ".", "=", "{", "}":
if t != string(term) && t != "/" {
return "", fmt.Errorf("expected %q but got %q", term, t)
}
case typeEOF:
if t != eof {
return "", fmt.Errorf("expected EOF but got %q", t)
}
case typeIdent:
if err := expectIdent(t); err != nil {
return "", err
}
case typeLiteral:
if err := expectPChars(t); err != nil {
return "", err
}
default:
return "", fmt.Errorf("unknown termType %q", term)
}
p.tokens = p.tokens[1:]
p.accepted = append(p.accepted, t)
return t, nil
}
// expectPChars determines if "t" consists of only pchars defined in RFC3986.
//
// https://www.ietf.org/rfc/rfc3986.txt, P.49
//
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
// / "*" / "+" / "," / ";" / "="
// pct-encoded = "%" HEXDIG HEXDIG
func expectPChars(t string) error {
const (
init = iota
pct1
pct2
)
st := init
for _, r := range t {
if st != init {
if !isHexDigit(r) {
return fmt.Errorf("invalid hexdigit: %c(%U)", r, r)
}
switch st {
case pct1:
st = pct2
case pct2:
st = init
}
continue
}
// unreserved
switch {
case 'A' <= r && r <= 'Z':
continue
case 'a' <= r && r <= 'z':
continue
case '0' <= r && r <= '9':
continue
}
switch r {
case '-', '.', '_', '~':
// unreserved
case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=':
// sub-delims
case ':', '@':
// rest of pchar
case '%':
// pct-encoded
st = pct1
default:
return fmt.Errorf("invalid character in path segment: %q(%U)", r, r)
}
}
if st != init {
return fmt.Errorf("invalid percent-encoding in %q", t)
}
return nil
}
// expectIdent determines if "ident" is a valid identifier in .proto schema ([[:alpha:]_][[:alphanum:]_]*).
func expectIdent(ident string) error {
if ident == "" {
return fmt.Errorf("empty identifier")
}
for pos, r := range ident {
switch {
case '0' <= r && r <= '9':
if pos == 0 {
return fmt.Errorf("identifier starting with digit: %s", ident)
}
continue
case 'A' <= r && r <= 'Z':
continue
case 'a' <= r && r <= 'z':
continue
case r == '_':
continue
default:
return fmt.Errorf("invalid character %q(%U) in identifier: %s", r, r, ident)
}
}
return nil
}
func isHexDigit(r rune) bool {
switch {
case '0' <= r && r <= '9':
return true
case 'A' <= r && r <= 'F':
return true
case 'a' <= r && r <= 'f':
return true
}
return false
}

View File

@ -1,321 +0,0 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/parse_test.go
import (
"flag"
"fmt"
"reflect"
"testing"
"go-micro.dev/v5/logger"
)
func TestTokenize(t *testing.T) {
for _, spec := range []struct {
src string
tokens []string
}{
{
src: "",
tokens: []string{eof},
},
{
src: "v1",
tokens: []string{"v1", eof},
},
{
src: "v1/b",
tokens: []string{"v1", "/", "b", eof},
},
{
src: "v1/endpoint/*",
tokens: []string{"v1", "/", "endpoint", "/", "*", eof},
},
{
src: "v1/endpoint/**",
tokens: []string{"v1", "/", "endpoint", "/", "**", eof},
},
{
src: "v1/b/{bucket_name=*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o",
eof,
},
},
{
src: "v1/b/{bucket_name=buckets/*}/o/{name}",
tokens: []string{
"v1", "/",
"b", "/",
"{", "bucket_name", "=", "buckets", "/", "*", "}", "/",
"o", "/", "{", "name", "}",
eof,
},
},
{
src: "v1/a=b&c=d;e=f:g/endpoint.rdf",
tokens: []string{
"v1", "/",
"a=b&c=d;e=f:g", "/",
"endpoint.rdf",
eof,
},
},
} {
tokens, verb := tokenize(spec.src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", spec.src, got, want)
}
if got, want := verb, ""; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", spec.src, got, want)
}
src := fmt.Sprintf("%s:%s", spec.src, "LOCK")
tokens, verb = tokenize(src)
if got, want := tokens, spec.tokens; !reflect.DeepEqual(got, want) {
t.Errorf("tokenize(%q) = %q, _; want %q, _", src, got, want)
}
if got, want := verb, "LOCK"; got != want {
t.Errorf("tokenize(%q) = _, %q; want _, %q", src, got, want)
}
}
}
func TestParseSegments(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
want []segment
}{
{
tokens: []string{"v1", eof},
want: []segment{
literal("v1"),
},
},
{
tokens: []string{"/", eof},
want: []segment{
wildcard{},
},
},
{
tokens: []string{"-._~!$&'()*+,;=:@", eof},
want: []segment{
literal("-._~!$&'()*+,;=:@"),
},
},
{
tokens: []string{"%e7%ac%ac%e4%b8%80%e7%89%88", eof},
want: []segment{
literal("%e7%ac%ac%e4%b8%80%e7%89%88"),
},
},
{
tokens: []string{"v1", "/", "*", eof},
want: []segment{
literal("v1"),
wildcard{},
},
},
{
tokens: []string{"v1", "/", "**", eof},
want: []segment{
literal("v1"),
deepWildcard{},
},
},
{
tokens: []string{"{", "name", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "field", ".", "nested", ".", "nested2", "=", "*", "}", eof},
want: []segment{
variable{
path: "field.nested.nested2",
segments: []segment{
wildcard{},
},
},
},
},
{
tokens: []string{"{", "name", "=", "a", "/", "b", "/", "*", "}", eof},
want: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
},
},
{
tokens: []string{
"v1", "/",
"{",
"name", ".", "nested", ".", "nested2",
"=",
"a", "/", "b", "/", "*",
"}", "/",
"o", "/",
"{",
"another_name",
"=",
"a", "/", "b", "/", "*", "/", "c",
"}", "/",
"**",
eof},
want: []segment{
literal("v1"),
variable{
path: "name.nested.nested2",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
},
},
literal("o"),
variable{
path: "another_name",
segments: []segment{
literal("a"),
literal("b"),
wildcard{},
literal("c"),
},
},
deepWildcard{},
},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err != nil {
t.Errorf("parser{%q}.segments() failed with %v; want success", spec.tokens, err)
continue
}
if got, want := segs, spec.want; !reflect.DeepEqual(got, want) {
t.Errorf("parser{%q}.segments() = %#v; want %#v", spec.tokens, got, want)
}
if got := p.tokens; len(got) > 0 {
t.Errorf("p.tokens = %q; want []; spec.tokens=%q", got, spec.tokens)
}
}
}
func TestParseSegmentsWithErrors(t *testing.T) {
flag.Set("v", "3")
for _, spec := range []struct {
tokens []string
}{
{
// double slash
tokens: []string{"//", eof},
},
{
// invalid literal
tokens: []string{"a?b", eof},
},
{
// invalid percent-encoding
tokens: []string{"%", eof},
},
{
// invalid percent-encoding
tokens: []string{"%2", eof},
},
{
// invalid percent-encoding
tokens: []string{"a%2z", eof},
},
{
// empty segments
tokens: []string{eof},
},
{
// unterminated variable
tokens: []string{"{", "name", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", eof},
},
{
// unterminated variable
tokens: []string{"{", "name", "=", "*", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", "}", eof},
},
{
// empty component in field path
tokens: []string{"{", "name", ".", ".", "nested", "}", eof},
},
{
// invalid character in identifier
tokens: []string{"{", "field-name", "}", eof},
},
{
// no slash between segments
tokens: []string{"v1", "endpoint", eof},
},
{
// no slash between segments
tokens: []string{"v1", "{", "name", "}", eof},
},
} {
p := parser{tokens: spec.tokens}
segs, err := p.topLevelSegments()
if err == nil {
t.Errorf("parser{%q}.segments() succeeded; want InvalidTemplateError; accepted %#v", spec.tokens, segs)
continue
}
logger.Info(err)
}
}

View File

@ -1,24 +0,0 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/utilities/pattern.go
// An OpCode is a opcode of compiled path patterns.
type OpCode int
// These constants are the valid values of OpCode.
const (
// OpNop does nothing.
OpNop = OpCode(iota)
// OpPush pushes a component to stack.
OpPush
// OpLitPush pushes a component to stack if it matches to the literal.
OpLitPush
// OpPushM concatenates the remaining components and pushes it to stack.
OpPushM
// OpConcatN pops N items from stack, concatenates them and pushes it back to stack.
OpConcatN
// OpCapture pops an item and binds it to the variable.
OpCapture
// OpEnd is the least positive invalid opcode.
OpEnd
)

View File

@ -1,282 +0,0 @@
package util
// download from https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/runtime/pattern.go
import (
"errors"
"fmt"
"strings"
log "go-micro.dev/v5/logger"
)
var (
// ErrNotMatch indicates that the given HTTP request path does not match to the pattern.
ErrNotMatch = errors.New("not match to the path pattern")
// ErrInvalidPattern indicates that the given definition of Pattern is not valid.
ErrInvalidPattern = errors.New("invalid pattern")
)
type rop struct {
code OpCode
operand int
}
// Pattern is a template pattern of http request paths defined in github.com/googleapis/googleapis/google/api/http.proto.
type Pattern struct {
// verb is the VERB part of the path pattern. It is empty if the pattern does not have VERB part.
verb string
// ops is a list of operations
ops []rop
// pool is a constant pool indexed by the operands or vars.
pool []string
// vars is a list of variables names to be bound by this pattern
vars []string
// stacksize is the max depth of the stack
stacksize int
// tailLen is the length of the fixed-size segments after a deep wildcard
tailLen int
// assumeColonVerb indicates whether a path suffix after a final
// colon may only be interpreted as a verb.
assumeColonVerb bool
}
type patternOptions struct {
logger log.Logger
assumeColonVerb bool
}
// PatternOpt is an option for creating Patterns.
type PatternOpt func(*patternOptions)
// PatternLogger sets the logger.
func PatternLogger(l log.Logger) PatternOpt {
return func(po *patternOptions) {
po.logger = l
}
}
// NewPattern returns a new Pattern from the given definition values.
// "ops" is a sequence of op codes. "pool" is a constant pool.
// "verb" is the verb part of the pattern. It is empty if the pattern does not have the part.
// "version" must be 1 for now.
// It returns an error if the given definition is invalid.
func NewPattern(version int, ops []int, pool []string, verb string, opts ...PatternOpt) (Pattern, error) {
options := patternOptions{
assumeColonVerb: true,
}
for _, o := range opts {
o(&options)
}
logger := log.LoggerOrDefault(options.logger)
if version != 1 {
logger.Logf(log.DebugLevel, "unsupported version: %d", version)
return Pattern{}, ErrInvalidPattern
}
l := len(ops)
if l%2 != 0 {
logger.Logf(log.DebugLevel, "odd number of ops codes: %d", l)
return Pattern{}, ErrInvalidPattern
}
var (
typedOps []rop
stack, maxstack int
tailLen int
pushMSeen bool
vars []string
)
for i := 0; i < l; i += 2 {
op := rop{code: OpCode(ops[i]), operand: ops[i+1]}
switch op.code {
case OpNop:
continue
case OpPush:
if pushMSeen {
tailLen++
}
stack++
case OpPushM:
if pushMSeen {
logger.Logf(log.DebugLevel, "pushM appears twice")
return Pattern{}, ErrInvalidPattern
}
pushMSeen = true
stack++
case OpLitPush:
if op.operand < 0 || len(pool) <= op.operand {
logger.Logf(log.DebugLevel, "negative literal index: %d", op.operand)
return Pattern{}, ErrInvalidPattern
}
if pushMSeen {
tailLen++
}
stack++
case OpConcatN:
if op.operand <= 0 {
logger.Logf(log.DebugLevel, "negative concat size: %d", op.operand)
return Pattern{}, ErrInvalidPattern
}
stack -= op.operand
if stack < 0 {
logger.Logf(log.DebugLevel, "stack underflow")
return Pattern{}, ErrInvalidPattern
}
stack++
case OpCapture:
if op.operand < 0 || len(pool) <= op.operand {
logger.Logf(log.DebugLevel, "variable name index out of bound: %d", op.operand)
return Pattern{}, ErrInvalidPattern
}
v := pool[op.operand]
op.operand = len(vars)
vars = append(vars, v)
stack--
if stack < 0 {
logger.Logf(log.DebugLevel, "stack underflow")
return Pattern{}, ErrInvalidPattern
}
default:
logger.Logf(log.DebugLevel, "invalid opcode: %d", op.code)
return Pattern{}, ErrInvalidPattern
}
if maxstack < stack {
maxstack = stack
}
typedOps = append(typedOps, op)
}
return Pattern{
ops: typedOps,
pool: pool,
vars: vars,
stacksize: maxstack,
tailLen: tailLen,
verb: verb,
assumeColonVerb: options.assumeColonVerb,
}, nil
}
// MustPattern is a helper function which makes it easier to call NewPattern in variable initialization.
func MustPattern(p Pattern, err error) Pattern {
if err != nil {
log.Logf(log.FatalLevel, "Pattern initialization failed: %v", err)
}
return p
}
// Match examines components if it matches to the Pattern.
// If it matches, the function returns a mapping from field paths to their captured values.
// If otherwise, the function returns an error.
func (p Pattern) Match(components []string, verb string) (map[string]string, error) {
if p.verb != verb {
if p.assumeColonVerb || p.verb != "" {
return nil, ErrNotMatch
}
if len(components) == 0 {
components = []string{":" + verb}
} else {
components = append([]string{}, components...)
components[len(components)-1] += ":" + verb
}
verb = ""
}
var pos int
stack := make([]string, 0, p.stacksize)
captured := make([]string, len(p.vars))
l := len(components)
for _, op := range p.ops {
switch op.code {
case OpNop:
continue
case OpPush, OpLitPush:
if pos >= l {
return nil, ErrNotMatch
}
c := components[pos]
if op.code == OpLitPush {
if lit := p.pool[op.operand]; c != lit {
return nil, ErrNotMatch
}
}
stack = append(stack, c)
pos++
case OpPushM:
end := len(components)
if end < pos+p.tailLen {
return nil, ErrNotMatch
}
end -= p.tailLen
stack = append(stack, strings.Join(components[pos:end], "/"))
pos = end
case OpConcatN:
n := op.operand
l := len(stack) - n
stack = append(stack[:l], strings.Join(stack[l:], "/"))
case OpCapture:
n := len(stack) - 1
captured[op.operand] = stack[n]
stack = stack[:n]
}
}
if pos < l {
return nil, ErrNotMatch
}
bindings := make(map[string]string)
for i, val := range captured {
bindings[p.vars[i]] = val
}
return bindings, nil
}
// Verb returns the verb part of the Pattern.
func (p Pattern) Verb() string { return p.verb }
func (p Pattern) String() string {
var stack []string
for _, op := range p.ops {
switch op.code {
case OpNop:
continue
case OpPush:
stack = append(stack, "*")
case OpLitPush:
stack = append(stack, p.pool[op.operand])
case OpPushM:
stack = append(stack, "**")
case OpConcatN:
n := op.operand
l := len(stack) - n
stack = append(stack[:l], strings.Join(stack[l:], "/"))
case OpCapture:
n := len(stack) - 1
stack[n] = fmt.Sprintf("{%s=%s}", p.vars[op.operand], stack[n])
}
}
segs := strings.Join(stack, "/")
if p.verb != "" {
return fmt.Sprintf("/%s:%s", segs, p.verb)
}
return "/" + segs
}
// AssumeColonVerbOpt indicates whether a path suffix after a final
// colon may only be interpreted as a verb.
func AssumeColonVerbOpt(val bool) PatternOpt {
return PatternOpt(func(o *patternOptions) {
o.assumeColonVerb = val
})
}

View File

@ -1,66 +0,0 @@
package util
// download from
// https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types.go
import (
"fmt"
"strings"
)
type template struct {
verb string
template string
segments []segment
}
type segment interface {
fmt.Stringer
compile() (ops []op)
}
type wildcard struct{}
type deepWildcard struct{}
type literal string
type variable struct {
path string
segments []segment
}
func (wildcard) String() string {
return "*"
}
func (deepWildcard) String() string {
return "**"
}
func (l literal) String() string {
return string(l)
}
func (v variable) String() string {
var segs []string
for _, s := range v.segments {
segs = append(segs, s.String())
}
return fmt.Sprintf("{%s=%s}", v.path, strings.Join(segs, "/"))
}
func (t template) String() string {
var segs []string
for _, s := range t.segments {
segs = append(segs, s.String())
}
str := strings.Join(segs, "/")
if t.verb != "" {
str = fmt.Sprintf("%s:%s", str, t.verb)
}
return "/" + str
}

View File

@ -1,94 +0,0 @@
package util
// download from
// https://raw.githubusercontent.com/grpc-ecosystem/grpc-gateway/master/protoc-gen-grpc-gateway/httprule/types_test.go
import (
"fmt"
"testing"
)
func TestTemplateStringer(t *testing.T) {
for _, spec := range []struct {
segs []segment
want string
}{
{
segs: []segment{
literal("v1"),
},
want: "/v1",
},
{
segs: []segment{
wildcard{},
},
want: "/*",
},
{
segs: []segment{
deepWildcard{},
},
want: "/**",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
},
},
},
want: "/{name=a}",
},
{
segs: []segment{
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
},
want: "/{name=a/*/b}",
},
{
segs: []segment{
literal("v1"),
variable{
path: "name",
segments: []segment{
literal("a"),
wildcard{},
literal("b"),
},
},
literal("c"),
variable{
path: "field.nested",
segments: []segment{
wildcard{},
literal("d"),
},
},
wildcard{},
literal("e"),
deepWildcard{},
},
want: "/v1/{name=a/*/b}/c/{field.nested=*/d}/*/e/**",
},
} {
tmpl := template{segments: spec.segs}
if got, want := tmpl.String(), spec.want; got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
tmpl.verb = "LOCK"
if got, want := tmpl.String(), fmt.Sprintf("%s:LOCK", spec.want); got != want {
t.Errorf("%#v.String() = %q; want %q", tmpl, got, want)
}
}
}

View File

@ -1,28 +0,0 @@
// Package acme abstracts away various ACME libraries
package acme
import (
"crypto/tls"
"errors"
"net"
)
var (
// ErrProviderNotImplemented can be returned when attempting to
// instantiate an unimplemented provider.
ErrProviderNotImplemented = errors.New("Provider not implemented")
)
// Provider is a ACME provider interface.
type Provider interface {
// Listen returns a new listener
Listen(...string) (net.Listener, error)
// TLSConfig returns a tls config
TLSConfig(...string) (*tls.Config, error)
}
// The Let's Encrypt ACME endpoints.
const (
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
)

View File

@ -1,47 +0,0 @@
// Package autocert is the ACME provider from golang.org/x/crypto/acme/autocert
// This provider does not take any config.
package autocert
import (
"crypto/tls"
"net"
"os"
"go-micro.dev/v5/api/server/acme"
log "go-micro.dev/v5/logger"
"golang.org/x/crypto/acme/autocert"
)
// autoCertACME is the ACME provider from golang.org/x/crypto/acme/autocert.
type autocertProvider struct {
logger log.Logger
}
// Listen implements acme.Provider.
func (a *autocertProvider) Listen(hosts ...string) (net.Listener, error) {
return autocert.NewListener(hosts...), nil
}
// TLSConfig returns a new tls config.
func (a *autocertProvider) TLSConfig(hosts ...string) (*tls.Config, error) {
logger := log.LoggerOrDefault(a.logger)
// create a new manager
m := &autocert.Manager{
Prompt: autocert.AcceptTOS,
}
if len(hosts) > 0 {
m.HostPolicy = autocert.HostWhitelist(hosts...)
}
dir := cacheDir()
if err := os.MkdirAll(dir, 0700); err != nil {
logger.Logf(log.InfoLevel, "warning: autocert not using a cache: %v", err)
} else {
m.Cache = autocert.DirCache(dir)
}
return m.TLSConfig(), nil
}
// New returns an autocert acme.Provider.
func NewProvider() acme.Provider {
return &autocertProvider{}
}

View File

@ -1,16 +0,0 @@
package autocert
import (
"testing"
)
func TestAutocert(t *testing.T) {
l := NewProvider()
if _, ok := l.(*autocertProvider); !ok {
t.Error("NewProvider() didn't return an autocertProvider")
}
// TODO: Travis CI doesn't let us bind :443
// if _, err := l.NewListener(); err != nil {
// t.Error(err.Error())
// }
}

View File

@ -1,37 +0,0 @@
package autocert
import (
"os"
"path/filepath"
"runtime"
)
func homeDir() string {
if runtime.GOOS == "windows" {
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
}
if h := os.Getenv("HOME"); h != "" {
return h
}
return "/"
}
func cacheDir() string {
const base = "golang-autocert"
switch runtime.GOOS {
case "darwin":
return filepath.Join(homeDir(), "Library", "Caches", base)
case "windows":
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
if v := os.Getenv(ev); v != "" {
return filepath.Join(v, base)
}
}
// Worst case:
return filepath.Join(homeDir(), base)
}
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
return filepath.Join(xdg, base)
}
return filepath.Join(homeDir(), ".cache", base)
}

View File

@ -1,86 +0,0 @@
package acme
import (
"github.com/go-acme/lego/v4/challenge"
"go-micro.dev/v5/logger"
)
// Option (or Options) are passed to New() to configure providers.
type Option func(o *Options)
// Options represents various options you can present to ACME providers.
type Options struct {
// ChallengeProvider is a go-acme/lego challenge provider. Set this if you
// want to use DNS Challenges. Otherwise, tls-alpn-01 will be used
ChallengeProvider challenge.Provider
// Cache is a storage interface. Most ACME libraries have an cache, but
// there's no defined interface, so if you consume this option
// sanity check it before using.
Cache interface{}
// Logger is the underling logging framework
Logger logger.Logger
// CA is the CA to use
CA string
// AcceptTLS must be set to true to indicate that you have read your
// provider's terms of service.
AcceptToS bool
// Issue certificates for domains on demand. Otherwise, certs will be
// retrieved / issued on start-up.
OnDemand bool
}
// AcceptToS indicates whether you accept your CA's terms of service.
func AcceptToS(b bool) Option {
return func(o *Options) {
o.AcceptToS = b
}
}
// CA sets the CA of an acme.Options.
func CA(CA string) Option {
return func(o *Options) {
o.CA = CA
}
}
// ChallengeProvider sets the Challenge provider of an acme.Options
// if set, it enables the DNS challenge, otherwise tls-alpn-01 will be used.
func ChallengeProvider(p challenge.Provider) Option {
return func(o *Options) {
o.ChallengeProvider = p
}
}
// OnDemand enables on-demand certificate issuance. Not recommended for use
// with the DNS challenge, as the first connection may be very slow.
func OnDemand(b bool) Option {
return func(o *Options) {
o.OnDemand = b
}
}
// Cache provides a cache / storage interface to the underlying ACME library
// as there is no standard, this needs to be validated by the underlying
// implementation.
func Cache(c interface{}) Option {
return func(o *Options) {
o.Cache = c
}
}
// Logger sets the underline logger.
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// DefaultOptions uses the Let's Encrypt Production CA, with DNS Challenge disabled.
func DefaultOptions() Options {
return Options{
AcceptToS: true,
CA: LetsEncryptProductionCA,
OnDemand: true,
}
}

View File

@ -1,57 +0,0 @@
package cors
import (
"net/http"
)
type Config struct {
AllowOrigin string
AllowMethods string
AllowHeaders string
AllowCredentials bool
}
// CombinedCORSHandler wraps a server and provides CORS headers.
func CombinedCORSHandler(h http.Handler, config *Config) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if config != nil {
SetHeaders(w, r, config)
}
if r.Method == "OPTIONS" {
return
}
h.ServeHTTP(w, r)
})
}
// SetHeaders sets the CORS headers.
func SetHeaders(w http.ResponseWriter, _ *http.Request, config *Config) {
set := func(w http.ResponseWriter, k, v string) {
if v := w.Header().Get(k); len(v) > 0 {
return
}
w.Header().Set(k, v)
}
// For forward-compatible code, default values may not be provided in the future
if config.AllowCredentials {
set(w, "Access-Control-Allow-Credentials", "true")
} else {
set(w, "Access-Control-Allow-Credentials", "false")
}
if config.AllowOrigin == "" {
set(w, "Access-Control-Allow-Origin", "*")
} else {
set(w, "Access-Control-Allow-Origin", config.AllowOrigin)
}
if config.AllowMethods == "" {
set(w, "Access-Control-Allow-Methods", "POST, PATCH, GET, OPTIONS, PUT, DELETE")
} else {
set(w, "Access-Control-Allow-Methods", config.AllowMethods)
}
if config.AllowHeaders == "" {
set(w, "Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
} else {
set(w, "Access-Control-Allow-Headers", config.AllowHeaders)
}
}

View File

@ -1,118 +0,0 @@
// Package http provides a http server with features; acme, cors, etc
package http
import (
"crypto/tls"
"net"
"net/http"
"os"
"sync"
"github.com/gorilla/handlers"
"go-micro.dev/v5/api/server"
"go-micro.dev/v5/api/server/cors"
log "go-micro.dev/v5/logger"
)
type httpServer struct {
opts server.Options
mux *http.ServeMux
exit chan chan error
address string
mtx sync.RWMutex
}
func NewServer(address string, opts ...server.Option) server.Server {
options := server.NewOptions(opts...)
return &httpServer{
opts: options,
mux: http.NewServeMux(),
address: address,
exit: make(chan chan error),
}
}
func (s *httpServer) Address() string {
s.mtx.RLock()
defer s.mtx.RUnlock()
return s.address
}
func (s *httpServer) Init(opts ...server.Option) error {
for _, o := range opts {
o(&s.opts)
}
return nil
}
func (s *httpServer) Handle(path string, handler http.Handler) {
// TODO: move this stuff out to one place with ServeHTTP
// apply the wrappers, e.g. auth
for _, wrapper := range s.opts.Wrappers {
handler = wrapper(handler)
}
// wrap with cors
if s.opts.EnableCORS {
handler = cors.CombinedCORSHandler(handler, s.opts.CORSConfig)
}
// wrap with logger
handler = handlers.CombinedLoggingHandler(os.Stdout, handler)
s.mux.Handle(path, handler)
}
func (s *httpServer) Start() error {
logger := s.opts.Logger
var l net.Listener
var err error
if s.opts.EnableACME && s.opts.ACMEProvider != nil {
// should we check the address to make sure its using :443?
l, err = s.opts.ACMEProvider.Listen(s.opts.ACMEHosts...)
} else if s.opts.EnableTLS && s.opts.TLSConfig != nil {
l, err = tls.Listen("tcp", s.address, s.opts.TLSConfig)
} else {
// otherwise plain listen
l, err = net.Listen("tcp", s.address)
}
if err != nil {
return err
}
logger.Logf(log.InfoLevel, "HTTP API Listening on %s", l.Addr().String())
s.mtx.Lock()
s.address = l.Addr().String()
s.mtx.Unlock()
go func() {
if err := http.Serve(l, s.mux); err != nil {
// temporary fix
// logger.Log(log.FatalLevel, err)
logger.Log(log.ErrorLevel, err)
}
}()
go func() {
ch := <-s.exit
ch <- l.Close()
}()
return nil
}
func (s *httpServer) Stop() error {
ch := make(chan error)
s.exit <- ch
return <-ch
}
func (s *httpServer) String() string {
return "http"
}

View File

@ -1,115 +0,0 @@
package http
import (
"fmt"
"io"
"net/http"
"testing"
"go-micro.dev/v5/api/server"
"go-micro.dev/v5/api/server/cors"
)
func TestHTTPServer(t *testing.T) {
testResponse := "hello world"
s := NewServer("localhost:0")
s.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, testResponse)
}))
if err := s.Start(); err != nil {
t.Fatal(err)
}
rsp, err := http.Get(fmt.Sprintf("http://%s/", s.Address()))
if err != nil {
t.Fatal(err)
}
defer rsp.Body.Close()
b, err := io.ReadAll(rsp.Body)
if err != nil {
t.Fatal(err)
}
if string(b) != testResponse {
t.Fatalf("Unexpected response, got %s, expected %s", string(b), testResponse)
}
if err := s.Stop(); err != nil {
t.Fatal(err)
}
}
func TestCORSHTTPServer(t *testing.T) {
testResponse := "hello world"
testAllowOrigin := "*"
testAllowCredentials := true
testAllowMethods := "GET"
testAllowHeaders := "Accept, Content-Type, Content-Length"
s := NewServer("localhost:0",
server.EnableCORS(true),
server.CORSConfig(&cors.Config{
AllowCredentials: testAllowCredentials,
AllowOrigin: testAllowOrigin,
AllowMethods: testAllowMethods,
AllowHeaders: testAllowHeaders,
}),
)
s.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, testResponse)
}))
if err := s.Start(); err != nil {
t.Fatal(err)
}
rsp, err := http.Get(fmt.Sprintf("http://%s/", s.Address()))
if err != nil {
t.Fatal(err)
}
defer rsp.Body.Close()
b, err := io.ReadAll(rsp.Body)
if err != nil {
t.Fatal(err)
}
if string(b) != testResponse {
t.Fatalf("Unexpected response, got %s, expected %s", string(b), testResponse)
}
allowCredentials := rsp.Header.Get("Access-Control-Allow-Credentials")
getTestCredentialsStr := func() string {
if testAllowCredentials == true {
return "true"
} else {
return "false"
}
}
if getTestCredentialsStr() != allowCredentials {
t.Fatalf("Unexpected Access-Control-Allow-Credentials, got %s, expected %s", allowCredentials, getTestCredentialsStr())
}
allowOrigin := rsp.Header.Get("Access-Control-Allow-Origin")
if testAllowOrigin != allowOrigin {
t.Fatalf("Unexpected Access-Control-Allow-Origins, got %s, expected %s", allowOrigin, testAllowOrigin)
}
allowMethods := rsp.Header.Get("Access-Control-Allow-Methods")
if testAllowMethods != allowMethods {
t.Fatalf("Unexpected Access-Control-Allow-Methods, got %s, expected %s", allowMethods, testAllowMethods)
}
allowHeaders := rsp.Header.Get("Access-Control-Allow-Headers")
if testAllowHeaders != allowHeaders {
t.Fatalf("Unexpected Access-Control-Allow-Headers, got %s, expected %s", allowHeaders, testAllowHeaders)
}
if err := s.Stop(); err != nil {
t.Fatal(err)
}
}

View File

@ -1,101 +0,0 @@
package server
import (
"crypto/tls"
"net/http"
"go-micro.dev/v5/api/resolver"
"go-micro.dev/v5/api/server/acme"
"go-micro.dev/v5/api/server/cors"
"go-micro.dev/v5/logger"
)
type Option func(o *Options)
type Options struct {
ACMEProvider acme.Provider
Resolver resolver.Resolver
Logger logger.Logger
CORSConfig *cors.Config
TLSConfig *tls.Config
ACMEHosts []string
Wrappers []Wrapper
EnableACME bool
EnableCORS bool
EnableTLS bool
}
type Wrapper func(h http.Handler) http.Handler
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
}
for _, o := range opts {
o(&options)
}
return options
}
func WrapHandler(w Wrapper) Option {
return func(o *Options) {
o.Wrappers = append(o.Wrappers, w)
}
}
func EnableCORS(b bool) Option {
return func(o *Options) {
o.EnableCORS = b
}
}
func CORSConfig(c *cors.Config) Option {
return func(o *Options) {
o.CORSConfig = c
}
}
func EnableACME(b bool) Option {
return func(o *Options) {
o.EnableACME = b
}
}
func ACMEHosts(hosts ...string) Option {
return func(o *Options) {
o.ACMEHosts = hosts
}
}
func ACMEProvider(p acme.Provider) Option {
return func(o *Options) {
o.ACMEProvider = p
}
}
func EnableTLS(b bool) Option {
return func(o *Options) {
o.EnableTLS = b
}
}
func TLSConfig(t *tls.Config) Option {
return func(o *Options) {
o.TLSConfig = t
}
}
func Resolver(r resolver.Resolver) Option {
return func(o *Options) {
o.Resolver = r
}
}
// Logger sets the underline logging framework.
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}

View File

@ -1,15 +0,0 @@
// Package server provides an API gateway server which handles inbound requests
package server
import (
"net/http"
)
// Server serves api requests.
type Server interface {
Address() string
Init(opts ...Option) error
Handle(path string, handler http.Handler)
Start() error
Stop() error
}

View File

@ -12,7 +12,6 @@ import (
import (
context "context"
api "go-micro.dev/v5/api"
client "go-micro.dev/v5/client"
server "go-micro.dev/v5/server"
)
@ -23,31 +22,10 @@ var _ = fmt.Errorf
var _ = math.Inf
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Greeter service
func NewGreeterEndpoints() []*api.Endpoint {
return []*api.Endpoint{
{
Name: "Greeter.Hello",
Path: []string{"/hello"},
Method: []string{"POST"},
Handler: "rpc",
},
{
Name: "Greeter.Stream",
Path: []string{"/stream"},
Method: []string{"GET"},
Stream: true,
Handler: "rpc",
},
}
}
// Client API for Greeter service
type GreeterService interface {
@ -144,19 +122,6 @@ func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler, opts ...server
greeter
}
h := &greeterHandler{hdlr}
opts = append(opts, api.WithEndpoint(&api.Endpoint{
Name: "Greeter.Hello",
Path: []string{"/hello"},
Method: []string{"POST"},
Handler: "rpc",
}))
opts = append(opts, api.WithEndpoint(&api.Endpoint{
Name: "Greeter.Stream",
Path: []string{"/stream"},
Method: []string{"GET"},
Stream: true,
Handler: "rpc",
}))
return s.Handle(s.NewHandler(&Greeter{h}, opts...))
}

View File

@ -15,7 +15,6 @@ import (
// Paths for packages used by code generated in this file,
// relative to the import_prefix of the generator.Generator.
const (
apiPkgPath = "go-micro.dev/v5/api"
contextPkgPath = "context"
clientPkgPath = "go-micro.dev/v5/client"
serverPkgPath = "go-micro.dev/v5/server"
@ -40,7 +39,6 @@ func (g *micro) Name() string {
// They may vary from the final path component of the import path
// if the name is used by other packages.
var (
apiPkg string
contextPkg string
clientPkg string
serverPkg string
@ -50,7 +48,6 @@ var (
// Init initializes the plugin.
func (g *micro) Init(gen *generator.Generator) {
g.gen = gen
apiPkg = generator.RegisterUniquePackageName("api", nil)
contextPkg = generator.RegisterUniquePackageName("context", nil)
clientPkg = generator.RegisterUniquePackageName("client", nil)
serverPkg = generator.RegisterUniquePackageName("server", nil)
@ -77,7 +74,6 @@ func (g *micro) Generate(file *generator.FileDescriptor) {
return
}
g.P("// Reference imports to suppress errors if they are not otherwise used.")
g.P("var _ ", apiPkg, ".Endpoint")
g.P("var _ ", contextPkg, ".Context")
g.P("var _ ", clientPkg, ".Option")
g.P("var _ ", serverPkg, ".Option")
@ -94,7 +90,6 @@ func (g *micro) GenerateImports(file *generator.FileDescriptor, imports map[gene
return
}
g.P("import (")
g.P(apiPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, apiPkgPath)))
g.P(contextPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, contextPkgPath)))
g.P(clientPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, clientPkgPath)))
g.P(serverPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, serverPkgPath)))
@ -143,23 +138,6 @@ func (g *micro) generateService(file *generator.FileDescriptor, service *pb.Serv
servAlias = strings.TrimSuffix(servAlias, "Service")
}
g.P()
g.P("// Api Endpoints for ", servName, " service")
g.P()
g.P("func New", servName, "Endpoints () []*", apiPkg, ".Endpoint {")
g.P("return []*", apiPkg, ".Endpoint{")
for _, method := range service.Method {
if method.Options != nil && proto.HasExtension(method.Options, options.E_Http) {
g.P("{")
g.generateEndpoint(servName, method)
g.P("},")
}
}
g.P("}")
g.P("}")
g.P()
g.P()
g.P("// Client API for ", servName, " service")
g.P()
@ -247,13 +225,6 @@ func (g *micro) generateService(file *generator.FileDescriptor, service *pb.Serv
g.P(unexport(servName))
g.P("}")
g.P("h := &", unexport(servName), "Handler{hdlr}")
for _, method := range service.Method {
if method.Options != nil && proto.HasExtension(method.Options, options.E_Http) {
g.P("opts = append(opts, ", apiPkg, ".WithEndpoint(&", apiPkg, ".Endpoint{")
g.generateEndpoint(servName, method)
g.P("}))")
}
}
g.P("return s.Handle(s.NewHandler(&", servName, "{h}, opts...))")
g.P("}")
g.P()

View File

@ -1,190 +0,0 @@
package file
import (
"bufio"
"context"
"fmt"
"io"
"os"
"go-micro.dev/v5/client"
"go-micro.dev/v5/logger"
proto "go-micro.dev/v5/util/file/proto"
)
// Client is the client interface to access files.
type File interface {
Open(filename string, truncate bool) (int64, error)
Stat(filename string) (*proto.StatResponse, error)
GetBlock(sessionId, blockId int64) ([]byte, error)
ReadAt(sessionId, offset, size int64) ([]byte, error)
Read(sessionId int64, buf []byte) (int, error)
Write(sessionId, offset int64, data []byte) error
Close(sessionId int64) error
Download(filename, saveFile string) error
Upload(filename, localFile string) error
DownloadAt(filename, saveFile string, blockId int) error
}
// NewClient returns a new Client which uses a micro Client.
func New(service string, c client.Client) File {
return &fc{proto.NewFileService(service, c)}
}
const (
blockSize = 512 * 1024
)
type fc struct {
c proto.FileService
}
func (c *fc) Open(filename string, truncate bool) (int64, error) {
rsp, err := c.c.Open(context.TODO(), &proto.OpenRequest{
Filename: filename,
Truncate: truncate,
})
if err != nil {
return 0, err
}
return rsp.Id, nil
}
func (c *fc) Stat(filename string) (*proto.StatResponse, error) {
return c.c.Stat(context.TODO(), &proto.StatRequest{Filename: filename})
}
func (c *fc) GetBlock(sessionId, blockId int64) ([]byte, error) {
return c.ReadAt(sessionId, blockId*blockSize, blockSize)
}
func (c *fc) ReadAt(sessionId, offset, size int64) ([]byte, error) {
rsp, err := c.c.Read(context.TODO(), &proto.ReadRequest{Id: sessionId, Size: size, Offset: offset})
if err != nil {
return nil, err
}
if rsp.Eof {
err = io.EOF
}
if rsp.Data == nil {
rsp.Data = make([]byte, size)
}
if size != rsp.Size {
return rsp.Data[:rsp.Size], err
}
return rsp.Data, nil
}
func (c *fc) Read(sessionId int64, buf []byte) (int, error) {
b, err := c.ReadAt(sessionId, 0, int64(cap(buf)))
if err != nil {
return 0, err
}
copy(buf, b)
return len(b), nil
}
func (c *fc) Write(sessionId, offset int64, data []byte) error {
_, err := c.c.Write(context.TODO(), &proto.WriteRequest{
Id: sessionId,
Offset: offset,
Data: data})
return err
}
func (c *fc) Close(sessionId int64) error {
_, err := c.c.Close(context.TODO(), &proto.CloseRequest{Id: sessionId})
return err
}
func (c *fc) Download(filename, saveFile string) error {
return c.DownloadAt(filename, saveFile, 0)
}
func (c *fc) Upload(filename, localFile string) error {
file, err := os.Open(localFile)
if err != nil {
return err
}
defer file.Close()
offset := 0
sessionId, err := c.Open(filename, true)
defer c.Close(sessionId)
if err != nil {
return err
}
reader := bufio.NewReader(file)
part := make([]byte, blockSize)
for {
count, err := reader.Read(part)
if err != nil {
break
}
err = c.Write(sessionId, int64(offset), part)
if err != nil {
return err
}
offset += count
}
if err != nil && err != io.EOF {
return fmt.Errorf("Error reading %v: %v", localFile, err)
}
return nil
}
func (c *fc) DownloadAt(filename, saveFile string, blockId int) error {
stat, err := c.Stat(filename)
if err != nil {
return err
}
if stat.Type == "Directory" {
return fmt.Errorf("%s is directory", filename)
}
blocks := int(stat.Size / blockSize)
if stat.Size%blockSize != 0 {
blocks += 1
}
logger.Logf(logger.InfoLevel, "Download %s in %d blocks\n", filename, blocks-blockId)
file, err := os.OpenFile(saveFile, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return err
}
defer file.Close()
sessionId, err := c.Open(filename, false)
if err != nil {
return err
}
for i := blockId; i < blocks; i++ {
buf, rerr := c.GetBlock(sessionId, int64(i))
if rerr != nil && rerr != io.EOF {
return rerr
}
if _, werr := file.WriteAt(buf, int64(i)*blockSize); werr != nil {
return werr
}
if i%((blocks-blockId)/100+1) == 0 {
logger.Logf(logger.InfoLevel, "Downloading %s [%d/%d] blocks", filename, i-blockId+1, blocks-blockId)
}
if rerr == io.EOF {
break
}
}
logger.Logf(logger.InfoLevel, "Download %s completed", filename)
c.Close(sessionId)
return nil
}

View File

@ -1,15 +0,0 @@
package file
import "os"
// Exists returns true if the path is existing.
func Exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return true, err
}

View File

@ -1,17 +0,0 @@
package file
import (
"testing"
)
func TestExists(t *testing.T) {
ok, err := Exists("/")
if ok {
return
}
if !ok || err != nil {
t.Fatalf("Test Exists fail, %s", err)
}
}

View File

@ -1,156 +0,0 @@
package file
import (
"io"
"os"
"path/filepath"
"sync"
"go-micro.dev/v5/errors"
log "go-micro.dev/v5/logger"
"go-micro.dev/v5/server"
proto "go-micro.dev/v5/util/file/proto"
"golang.org/x/net/context"
)
// NewHandler is a handler that can be registered with a micro Server.
func NewHandler(readDir string) proto.FileHandler {
return &handler{
readDir: readDir,
session: &session{
files: make(map[int64]*os.File),
},
logger: log.DefaultLogger,
}
}
// RegisterHandler is a convenience method for registering a handler.
func RegisterHandler(s server.Server, readDir string) {
proto.RegisterFileHandler(s, NewHandler(readDir))
}
type handler struct {
logger log.Logger
session *session
readDir string
}
func (h *handler) Open(ctx context.Context, req *proto.OpenRequest, rsp *proto.OpenResponse) error {
path := filepath.Join(h.readDir, req.Filename)
flags := os.O_CREATE | os.O_RDWR
if req.GetTruncate() {
flags = flags | os.O_TRUNC
}
file, err := os.OpenFile(path, flags, 0666)
if err != nil {
return errors.InternalServerError("go.micro.server", err.Error())
}
rsp.Id = h.session.Add(file)
rsp.Result = true
h.logger.Logf(log.DebugLevel, "Open %s, sessionId=%d", req.Filename, rsp.Id)
return nil
}
func (h *handler) Close(ctx context.Context, req *proto.CloseRequest, rsp *proto.CloseResponse) error {
h.session.Delete(req.Id)
h.logger.Logf(log.DebugLevel, "Close sessionId=%d", req.Id)
return nil
}
func (h *handler) Stat(ctx context.Context, req *proto.StatRequest, rsp *proto.StatResponse) error {
path := filepath.Join(h.readDir, req.Filename)
fi, err := os.Stat(path)
if os.IsNotExist(err) {
return errors.InternalServerError("go.micro.srv.file", err.Error())
}
if fi.IsDir() {
rsp.Type = "Directory"
} else {
rsp.Type = "File"
rsp.Size = fi.Size()
}
rsp.LastModified = fi.ModTime().Unix()
h.logger.Logf(log.DebugLevel, "Stat %s, %#v", req.Filename, rsp)
return nil
}
func (h *handler) Read(ctx context.Context, req *proto.ReadRequest, rsp *proto.ReadResponse) error {
file := h.session.Get(req.Id)
if file == nil {
return errors.InternalServerError("go.micro.srv.file", "You must call open first.")
}
rsp.Data = make([]byte, req.Size)
n, err := file.ReadAt(rsp.Data, req.Offset)
if err != nil && err != io.EOF {
return errors.InternalServerError("go.micro.srv.file", err.Error())
}
if err == io.EOF {
rsp.Eof = true
}
rsp.Size = int64(n)
rsp.Data = rsp.Data[:n]
h.logger.Logf(log.DebugLevel, "Read sessionId=%d, Offset=%d, n=%d", req.Id, req.Offset, rsp.Size)
return nil
}
func (h *handler) Write(ctx context.Context, req *proto.WriteRequest, rsp *proto.WriteResponse) error {
file := h.session.Get(req.Id)
if file == nil {
return errors.InternalServerError("go.micro.srv.file", "You must call open first.")
}
if _, err := file.WriteAt(req.GetData(), req.GetOffset()); err != nil {
return err
}
h.logger.Logf(log.DebugLevel, "Write sessionId=%d, Offset=%d, n=%d", req.Id, req.Offset)
return nil
}
type session struct {
files map[int64]*os.File
counter int64
sync.Mutex
}
func (s *session) Add(file *os.File) int64 {
s.Lock()
defer s.Unlock()
s.counter += 1
s.files[s.counter] = file
return s.counter
}
func (s *session) Get(id int64) *os.File {
s.Lock()
defer s.Unlock()
return s.files[id]
}
func (s *session) Delete(id int64) {
s.Lock()
defer s.Unlock()
if file, exist := s.files[id]; exist {
file.Close()
delete(s.files, id)
}
}
func (s *session) Len() int {
return len(s.files)
}

View File

@ -1,626 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: file.proto
package go_micro_server
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
type OpenRequest struct {
Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"`
Truncate bool `protobuf:"varint,2,opt,name=truncate,proto3" json:"truncate,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *OpenRequest) Reset() { *m = OpenRequest{} }
func (m *OpenRequest) String() string { return proto.CompactTextString(m) }
func (*OpenRequest) ProtoMessage() {}
func (*OpenRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{0}
}
func (m *OpenRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OpenRequest.Unmarshal(m, b)
}
func (m *OpenRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OpenRequest.Marshal(b, m, deterministic)
}
func (m *OpenRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_OpenRequest.Merge(m, src)
}
func (m *OpenRequest) XXX_Size() int {
return xxx_messageInfo_OpenRequest.Size(m)
}
func (m *OpenRequest) XXX_DiscardUnknown() {
xxx_messageInfo_OpenRequest.DiscardUnknown(m)
}
var xxx_messageInfo_OpenRequest proto.InternalMessageInfo
func (m *OpenRequest) GetFilename() string {
if m != nil {
return m.Filename
}
return ""
}
func (m *OpenRequest) GetTruncate() bool {
if m != nil {
return m.Truncate
}
return false
}
type OpenResponse struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Result bool `protobuf:"varint,2,opt,name=result,proto3" json:"result,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *OpenResponse) Reset() { *m = OpenResponse{} }
func (m *OpenResponse) String() string { return proto.CompactTextString(m) }
func (*OpenResponse) ProtoMessage() {}
func (*OpenResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{1}
}
func (m *OpenResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OpenResponse.Unmarshal(m, b)
}
func (m *OpenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OpenResponse.Marshal(b, m, deterministic)
}
func (m *OpenResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_OpenResponse.Merge(m, src)
}
func (m *OpenResponse) XXX_Size() int {
return xxx_messageInfo_OpenResponse.Size(m)
}
func (m *OpenResponse) XXX_DiscardUnknown() {
xxx_messageInfo_OpenResponse.DiscardUnknown(m)
}
var xxx_messageInfo_OpenResponse proto.InternalMessageInfo
func (m *OpenResponse) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *OpenResponse) GetResult() bool {
if m != nil {
return m.Result
}
return false
}
type CloseRequest struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CloseRequest) Reset() { *m = CloseRequest{} }
func (m *CloseRequest) String() string { return proto.CompactTextString(m) }
func (*CloseRequest) ProtoMessage() {}
func (*CloseRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{2}
}
func (m *CloseRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CloseRequest.Unmarshal(m, b)
}
func (m *CloseRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CloseRequest.Marshal(b, m, deterministic)
}
func (m *CloseRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_CloseRequest.Merge(m, src)
}
func (m *CloseRequest) XXX_Size() int {
return xxx_messageInfo_CloseRequest.Size(m)
}
func (m *CloseRequest) XXX_DiscardUnknown() {
xxx_messageInfo_CloseRequest.DiscardUnknown(m)
}
var xxx_messageInfo_CloseRequest proto.InternalMessageInfo
func (m *CloseRequest) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
type CloseResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CloseResponse) Reset() { *m = CloseResponse{} }
func (m *CloseResponse) String() string { return proto.CompactTextString(m) }
func (*CloseResponse) ProtoMessage() {}
func (*CloseResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{3}
}
func (m *CloseResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CloseResponse.Unmarshal(m, b)
}
func (m *CloseResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CloseResponse.Marshal(b, m, deterministic)
}
func (m *CloseResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_CloseResponse.Merge(m, src)
}
func (m *CloseResponse) XXX_Size() int {
return xxx_messageInfo_CloseResponse.Size(m)
}
func (m *CloseResponse) XXX_DiscardUnknown() {
xxx_messageInfo_CloseResponse.DiscardUnknown(m)
}
var xxx_messageInfo_CloseResponse proto.InternalMessageInfo
type StatRequest struct {
Filename string `protobuf:"bytes,1,opt,name=filename,proto3" json:"filename,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StatRequest) Reset() { *m = StatRequest{} }
func (m *StatRequest) String() string { return proto.CompactTextString(m) }
func (*StatRequest) ProtoMessage() {}
func (*StatRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{4}
}
func (m *StatRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatRequest.Unmarshal(m, b)
}
func (m *StatRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_StatRequest.Marshal(b, m, deterministic)
}
func (m *StatRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_StatRequest.Merge(m, src)
}
func (m *StatRequest) XXX_Size() int {
return xxx_messageInfo_StatRequest.Size(m)
}
func (m *StatRequest) XXX_DiscardUnknown() {
xxx_messageInfo_StatRequest.DiscardUnknown(m)
}
var xxx_messageInfo_StatRequest proto.InternalMessageInfo
func (m *StatRequest) GetFilename() string {
if m != nil {
return m.Filename
}
return ""
}
type StatResponse struct {
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
LastModified int64 `protobuf:"varint,3,opt,name=last_modified,json=lastModified,proto3" json:"last_modified,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StatResponse) Reset() { *m = StatResponse{} }
func (m *StatResponse) String() string { return proto.CompactTextString(m) }
func (*StatResponse) ProtoMessage() {}
func (*StatResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{5}
}
func (m *StatResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StatResponse.Unmarshal(m, b)
}
func (m *StatResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_StatResponse.Marshal(b, m, deterministic)
}
func (m *StatResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_StatResponse.Merge(m, src)
}
func (m *StatResponse) XXX_Size() int {
return xxx_messageInfo_StatResponse.Size(m)
}
func (m *StatResponse) XXX_DiscardUnknown() {
xxx_messageInfo_StatResponse.DiscardUnknown(m)
}
var xxx_messageInfo_StatResponse proto.InternalMessageInfo
func (m *StatResponse) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *StatResponse) GetSize() int64 {
if m != nil {
return m.Size
}
return 0
}
func (m *StatResponse) GetLastModified() int64 {
if m != nil {
return m.LastModified
}
return 0
}
type ReadRequest struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"`
Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReadRequest) Reset() { *m = ReadRequest{} }
func (m *ReadRequest) String() string { return proto.CompactTextString(m) }
func (*ReadRequest) ProtoMessage() {}
func (*ReadRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{6}
}
func (m *ReadRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReadRequest.Unmarshal(m, b)
}
func (m *ReadRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReadRequest.Marshal(b, m, deterministic)
}
func (m *ReadRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReadRequest.Merge(m, src)
}
func (m *ReadRequest) XXX_Size() int {
return xxx_messageInfo_ReadRequest.Size(m)
}
func (m *ReadRequest) XXX_DiscardUnknown() {
xxx_messageInfo_ReadRequest.DiscardUnknown(m)
}
var xxx_messageInfo_ReadRequest proto.InternalMessageInfo
func (m *ReadRequest) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *ReadRequest) GetOffset() int64 {
if m != nil {
return m.Offset
}
return 0
}
func (m *ReadRequest) GetSize() int64 {
if m != nil {
return m.Size
}
return 0
}
type ReadResponse struct {
Size int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
Eof bool `protobuf:"varint,3,opt,name=eof,proto3" json:"eof,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReadResponse) Reset() { *m = ReadResponse{} }
func (m *ReadResponse) String() string { return proto.CompactTextString(m) }
func (*ReadResponse) ProtoMessage() {}
func (*ReadResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{7}
}
func (m *ReadResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReadResponse.Unmarshal(m, b)
}
func (m *ReadResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReadResponse.Marshal(b, m, deterministic)
}
func (m *ReadResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReadResponse.Merge(m, src)
}
func (m *ReadResponse) XXX_Size() int {
return xxx_messageInfo_ReadResponse.Size(m)
}
func (m *ReadResponse) XXX_DiscardUnknown() {
xxx_messageInfo_ReadResponse.DiscardUnknown(m)
}
var xxx_messageInfo_ReadResponse proto.InternalMessageInfo
func (m *ReadResponse) GetSize() int64 {
if m != nil {
return m.Size
}
return 0
}
func (m *ReadResponse) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func (m *ReadResponse) GetEof() bool {
if m != nil {
return m.Eof
}
return false
}
type GetRequest struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
BlockId int64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetRequest) Reset() { *m = GetRequest{} }
func (m *GetRequest) String() string { return proto.CompactTextString(m) }
func (*GetRequest) ProtoMessage() {}
func (*GetRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{8}
}
func (m *GetRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetRequest.Unmarshal(m, b)
}
func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic)
}
func (m *GetRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetRequest.Merge(m, src)
}
func (m *GetRequest) XXX_Size() int {
return xxx_messageInfo_GetRequest.Size(m)
}
func (m *GetRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GetRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GetRequest proto.InternalMessageInfo
func (m *GetRequest) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *GetRequest) GetBlockId() int64 {
if m != nil {
return m.BlockId
}
return 0
}
type GetResponse struct {
BlockId int64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"`
Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetResponse) Reset() { *m = GetResponse{} }
func (m *GetResponse) String() string { return proto.CompactTextString(m) }
func (*GetResponse) ProtoMessage() {}
func (*GetResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{9}
}
func (m *GetResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetResponse.Unmarshal(m, b)
}
func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic)
}
func (m *GetResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetResponse.Merge(m, src)
}
func (m *GetResponse) XXX_Size() int {
return xxx_messageInfo_GetResponse.Size(m)
}
func (m *GetResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GetResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GetResponse proto.InternalMessageInfo
func (m *GetResponse) GetBlockId() int64 {
if m != nil {
return m.BlockId
}
return 0
}
func (m *GetResponse) GetSize() int64 {
if m != nil {
return m.Size
}
return 0
}
func (m *GetResponse) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type WriteRequest struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"`
Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WriteRequest) Reset() { *m = WriteRequest{} }
func (m *WriteRequest) String() string { return proto.CompactTextString(m) }
func (*WriteRequest) ProtoMessage() {}
func (*WriteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{10}
}
func (m *WriteRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WriteRequest.Unmarshal(m, b)
}
func (m *WriteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_WriteRequest.Marshal(b, m, deterministic)
}
func (m *WriteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_WriteRequest.Merge(m, src)
}
func (m *WriteRequest) XXX_Size() int {
return xxx_messageInfo_WriteRequest.Size(m)
}
func (m *WriteRequest) XXX_DiscardUnknown() {
xxx_messageInfo_WriteRequest.DiscardUnknown(m)
}
var xxx_messageInfo_WriteRequest proto.InternalMessageInfo
func (m *WriteRequest) GetId() int64 {
if m != nil {
return m.Id
}
return 0
}
func (m *WriteRequest) GetOffset() int64 {
if m != nil {
return m.Offset
}
return 0
}
func (m *WriteRequest) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
type WriteResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *WriteResponse) Reset() { *m = WriteResponse{} }
func (m *WriteResponse) String() string { return proto.CompactTextString(m) }
func (*WriteResponse) ProtoMessage() {}
func (*WriteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_9188e3b7e55e1162, []int{11}
}
func (m *WriteResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WriteResponse.Unmarshal(m, b)
}
func (m *WriteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_WriteResponse.Marshal(b, m, deterministic)
}
func (m *WriteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_WriteResponse.Merge(m, src)
}
func (m *WriteResponse) XXX_Size() int {
return xxx_messageInfo_WriteResponse.Size(m)
}
func (m *WriteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_WriteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_WriteResponse proto.InternalMessageInfo
func init() {
proto.RegisterType((*OpenRequest)(nil), "go.micro.server.OpenRequest")
proto.RegisterType((*OpenResponse)(nil), "go.micro.server.OpenResponse")
proto.RegisterType((*CloseRequest)(nil), "go.micro.server.CloseRequest")
proto.RegisterType((*CloseResponse)(nil), "go.micro.server.CloseResponse")
proto.RegisterType((*StatRequest)(nil), "go.micro.server.StatRequest")
proto.RegisterType((*StatResponse)(nil), "go.micro.server.StatResponse")
proto.RegisterType((*ReadRequest)(nil), "go.micro.server.ReadRequest")
proto.RegisterType((*ReadResponse)(nil), "go.micro.server.ReadResponse")
proto.RegisterType((*GetRequest)(nil), "go.micro.server.GetRequest")
proto.RegisterType((*GetResponse)(nil), "go.micro.server.GetResponse")
proto.RegisterType((*WriteRequest)(nil), "go.micro.server.WriteRequest")
proto.RegisterType((*WriteResponse)(nil), "go.micro.server.WriteResponse")
}
func init() { proto.RegisterFile("file.proto", fileDescriptor_9188e3b7e55e1162) }
var fileDescriptor_9188e3b7e55e1162 = []byte{
// 437 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0xcd, 0x8e, 0xd3, 0x30,
0x14, 0x85, 0x9b, 0x1f, 0x86, 0x70, 0x93, 0x52, 0xe4, 0x05, 0x2a, 0x15, 0x8c, 0x46, 0x66, 0x33,
0x6c, 0xb2, 0x00, 0x09, 0x1e, 0x00, 0x15, 0x5a, 0x24, 0x04, 0x32, 0x0b, 0x16, 0x2c, 0xaa, 0xb4,
0xbe, 0x41, 0x16, 0x69, 0x1c, 0x62, 0x17, 0x09, 0x5e, 0x9a, 0x57, 0x40, 0x76, 0xdc, 0xd6, 0x6d,
0x13, 0x09, 0xcd, 0xce, 0xd7, 0xf7, 0xf8, 0xf3, 0xb1, 0x73, 0x1c, 0x80, 0x52, 0x54, 0x98, 0x37,
0xad, 0xd4, 0x92, 0x4c, 0xbe, 0xcb, 0x7c, 0x2b, 0x36, 0xad, 0xcc, 0x15, 0xb6, 0xbf, 0xb0, 0xa5,
0x73, 0x48, 0x3f, 0x35, 0x58, 0x33, 0xfc, 0xb9, 0x43, 0xa5, 0xc9, 0x0c, 0x12, 0xa3, 0xae, 0x8b,
0x2d, 0x4e, 0x83, 0x9b, 0xe0, 0xf6, 0x01, 0x3b, 0xd4, 0xa6, 0xa7, 0xdb, 0x5d, 0xbd, 0x29, 0x34,
0x4e, 0xc3, 0x9b, 0xe0, 0x36, 0x61, 0x87, 0x9a, 0xbe, 0x86, 0xac, 0xc3, 0xa8, 0x46, 0xd6, 0x0a,
0xc9, 0x43, 0x08, 0x05, 0xb7, 0x84, 0x88, 0x85, 0x82, 0x93, 0xc7, 0x70, 0xd5, 0xa2, 0xda, 0x55,
0xda, 0xad, 0x74, 0x15, 0xbd, 0x86, 0xec, 0x6d, 0x25, 0x15, 0xee, 0xf7, 0x3f, 0x5b, 0x47, 0x27,
0x30, 0x76, 0xfd, 0x0e, 0x4c, 0x5f, 0x40, 0xfa, 0x45, 0x17, 0xfa, 0x3f, 0xfc, 0xd2, 0x6f, 0x90,
0x75, 0x52, 0xe7, 0x89, 0x40, 0xac, 0x7f, 0x37, 0x7b, 0x9d, 0x1d, 0x9b, 0x39, 0x25, 0xfe, 0x74,
0xe7, 0x89, 0x98, 0x1d, 0x93, 0xe7, 0x30, 0xae, 0x0a, 0xa5, 0x57, 0x5b, 0xc9, 0x45, 0x29, 0x90,
0x4f, 0x23, 0xdb, 0xcc, 0xcc, 0xe4, 0x47, 0x37, 0x47, 0x97, 0x90, 0x32, 0x2c, 0xf8, 0x80, 0x6f,
0x73, 0x5e, 0x59, 0x96, 0x0a, 0xb5, 0x23, 0xbb, 0xea, 0xb0, 0x5f, 0x74, 0xdc, 0x8f, 0x2e, 0x20,
0xeb, 0x50, 0x47, 0x9f, 0x56, 0x13, 0x78, 0x9e, 0x08, 0xc4, 0xbc, 0xd0, 0x85, 0xa5, 0x65, 0xcc,
0x8e, 0xc9, 0x23, 0x88, 0x50, 0x96, 0x16, 0x95, 0x30, 0x33, 0xa4, 0x6f, 0x00, 0xde, 0xa3, 0x1e,
0xf2, 0xf4, 0x04, 0x92, 0x75, 0x25, 0x37, 0x3f, 0x56, 0x82, 0x3b, 0x57, 0xf7, 0x6d, 0xbd, 0xe4,
0xf4, 0x33, 0xa4, 0x76, 0xa1, 0x73, 0xe0, 0x2b, 0x83, 0x13, 0x65, 0xef, 0x85, 0xed, 0xcd, 0x45,
0x47, 0x73, 0xf4, 0x03, 0x64, 0x5f, 0x5b, 0xa1, 0xf1, 0x0e, 0x17, 0x74, 0xc1, 0x9a, 0xc0, 0xd8,
0xb1, 0x3a, 0x7f, 0x2f, 0xff, 0x86, 0x10, 0xbf, 0x13, 0x15, 0x92, 0x39, 0xc4, 0x26, 0x76, 0xe4,
0x69, 0x7e, 0x96, 0xeb, 0xdc, 0x0b, 0xf5, 0xec, 0xd9, 0x40, 0xd7, 0x45, 0x6a, 0x64, 0x30, 0x26,
0x29, 0x3d, 0x18, 0x2f, 0x6b, 0x3d, 0x18, 0x3f, 0x5e, 0x1d, 0xc6, 0x7c, 0xc8, 0x1e, 0x8c, 0x17,
0x95, 0x1e, 0x8c, 0xff, 0xf5, 0xe9, 0x88, 0x2c, 0xe0, 0x9e, 0x3d, 0x2e, 0xb9, 0x54, 0xfa, 0x57,
0x3a, 0xbb, 0x1e, 0x6a, 0xfb, 0x24, 0xfb, 0x7a, 0x7a, 0x48, 0xfe, 0xab, 0xeb, 0x21, 0x9d, 0x3e,
0xba, 0xd1, 0xfa, 0xca, 0xfe, 0x3e, 0x5e, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x5c, 0xf0, 0x71,
0xa2, 0x4c, 0x04, 0x00, 0x00,
}

View File

@ -1,161 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: file.proto
package go_micro_server
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "go-micro.dev/v5/api"
client "go-micro.dev/v5/client"
server "go-micro.dev/v5/server"
)
// 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
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for File service
func NewFileEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for File service
type FileService interface {
Open(ctx context.Context, in *OpenRequest, opts ...client.CallOption) (*OpenResponse, error)
Stat(ctx context.Context, in *StatRequest, opts ...client.CallOption) (*StatResponse, error)
Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error)
Write(ctx context.Context, in *WriteRequest, opts ...client.CallOption) (*WriteResponse, error)
Close(ctx context.Context, in *CloseRequest, opts ...client.CallOption) (*CloseResponse, error)
}
type fileService struct {
c client.Client
name string
}
func NewFileService(name string, c client.Client) FileService {
return &fileService{
c: c,
name: name,
}
}
func (c *fileService) Open(ctx context.Context, in *OpenRequest, opts ...client.CallOption) (*OpenResponse, error) {
req := c.c.NewRequest(c.name, "File.Open", in)
out := new(OpenResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *fileService) Stat(ctx context.Context, in *StatRequest, opts ...client.CallOption) (*StatResponse, error) {
req := c.c.NewRequest(c.name, "File.Stat", in)
out := new(StatResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *fileService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) {
req := c.c.NewRequest(c.name, "File.Read", in)
out := new(ReadResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *fileService) Write(ctx context.Context, in *WriteRequest, opts ...client.CallOption) (*WriteResponse, error) {
req := c.c.NewRequest(c.name, "File.Write", in)
out := new(WriteResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *fileService) Close(ctx context.Context, in *CloseRequest, opts ...client.CallOption) (*CloseResponse, error) {
req := c.c.NewRequest(c.name, "File.Close", in)
out := new(CloseResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for File service
type FileHandler interface {
Open(context.Context, *OpenRequest, *OpenResponse) error
Stat(context.Context, *StatRequest, *StatResponse) error
Read(context.Context, *ReadRequest, *ReadResponse) error
Write(context.Context, *WriteRequest, *WriteResponse) error
Close(context.Context, *CloseRequest, *CloseResponse) error
}
func RegisterFileHandler(s server.Server, hdlr FileHandler, opts ...server.HandlerOption) error {
type file interface {
Open(ctx context.Context, in *OpenRequest, out *OpenResponse) error
Stat(ctx context.Context, in *StatRequest, out *StatResponse) error
Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error
Write(ctx context.Context, in *WriteRequest, out *WriteResponse) error
Close(ctx context.Context, in *CloseRequest, out *CloseResponse) error
}
type File struct {
file
}
h := &fileHandler{hdlr}
return s.Handle(s.NewHandler(&File{h}, opts...))
}
type fileHandler struct {
FileHandler
}
func (h *fileHandler) Open(ctx context.Context, in *OpenRequest, out *OpenResponse) error {
return h.FileHandler.Open(ctx, in, out)
}
func (h *fileHandler) Stat(ctx context.Context, in *StatRequest, out *StatResponse) error {
return h.FileHandler.Stat(ctx, in, out)
}
func (h *fileHandler) Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error {
return h.FileHandler.Read(ctx, in, out)
}
func (h *fileHandler) Write(ctx context.Context, in *WriteRequest, out *WriteResponse) error {
return h.FileHandler.Write(ctx, in, out)
}
func (h *fileHandler) Close(ctx context.Context, in *CloseRequest, out *CloseResponse) error {
return h.FileHandler.Close(ctx, in, out)
}

View File

@ -1,69 +0,0 @@
syntax = "proto3";
package go.micro.server;
service File {
rpc Open(OpenRequest) returns(OpenResponse) {};
rpc Stat(StatRequest) returns(StatResponse) {};
rpc Read(ReadRequest) returns(ReadResponse) {};
rpc Write(WriteRequest) returns(WriteResponse) {};
rpc Close(CloseRequest) returns(CloseResponse) {};
}
message OpenRequest {
string filename = 1;
bool truncate = 2;
}
message OpenResponse {
int64 id = 1;
bool result = 2;
}
message CloseRequest {
int64 id = 1;
}
message CloseResponse {
}
message StatRequest {
string filename = 1;
}
message StatResponse {
string type = 1;
int64 size = 2;
int64 last_modified = 3;
}
message ReadRequest {
int64 id = 1;
int64 offset = 2;
int64 size = 3;
}
message ReadResponse {
int64 size = 1;
bytes data = 2;
bool eof = 3;
}
message GetRequest {
int64 id = 1;
int64 block_id = 2;
}
message GetResponse {
int64 block_id = 1;
int64 size = 2;
bytes data = 3;
}
message WriteRequest {
int64 id = 1;
int64 offset = 2;
bytes data = 3;
}
message WriteResponse {}