github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/api/api.go (about)

     1  // Licensed under the Apache License, Version 2.0 (the "License");
     2  // you may not use this file except in compliance with the License.
     3  // You may obtain a copy of the License at
     4  //
     5  //     https://www.apache.org/licenses/LICENSE-2.0
     6  //
     7  // Unless required by applicable law or agreed to in writing, software
     8  // distributed under the License is distributed on an "AS IS" BASIS,
     9  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  // See the License for the specific language governing permissions and
    11  // limitations under the License.
    12  //
    13  // Original source: github.com/micro/go-micro/v3/api/api.go
    14  
    15  package api
    16  
    17  import (
    18  	"crypto/tls"
    19  	"encoding/json"
    20  	"errors"
    21  	"io"
    22  	"io/ioutil"
    23  	"net/http"
    24  	"regexp"
    25  	"strings"
    26  
    27  	jsonpatch "github.com/evanphx/json-patch/v5"
    28  
    29  	"github.com/tickoalcantara12/micro/v3/service/api/resolver"
    30  	"github.com/tickoalcantara12/micro/v3/service/context/metadata"
    31  	"github.com/tickoalcantara12/micro/v3/service/registry"
    32  	"github.com/tickoalcantara12/micro/v3/service/server"
    33  	"github.com/tickoalcantara12/micro/v3/util/acme"
    34  	"github.com/tickoalcantara12/micro/v3/util/codec"
    35  	"github.com/tickoalcantara12/micro/v3/util/codec/bytes"
    36  	"github.com/tickoalcantara12/micro/v3/util/codec/jsonrpc"
    37  	"github.com/tickoalcantara12/micro/v3/util/codec/protorpc"
    38  	"github.com/tickoalcantara12/micro/v3/util/qson"
    39  	"github.com/oxtoacart/bpool"
    40  )
    41  
    42  var (
    43  	bufferPool = bpool.NewSizedBufferPool(1024, 8)
    44  )
    45  
    46  type buffer struct {
    47  	io.ReadCloser
    48  }
    49  
    50  func (b *buffer) Write(_ []byte) (int, error) {
    51  	return 0, nil
    52  }
    53  
    54  type API interface {
    55  	// Initialise options
    56  	Init(...Option) error
    57  	// Get the options
    58  	Options() Options
    59  	// Register a http handler
    60  	Register(*Endpoint) error
    61  	// Register a route
    62  	Deregister(*Endpoint) error
    63  	// Implementation of api
    64  	String() string
    65  }
    66  
    67  // Server serves api requests
    68  type Server interface {
    69  	Address() string
    70  	Init(opts ...Option) error
    71  	Handle(path string, handler http.Handler)
    72  	Start() error
    73  	Stop() error
    74  }
    75  
    76  type Options struct {
    77  	EnableACME   bool
    78  	EnableCORS   bool
    79  	ACMEProvider acme.Provider
    80  	EnableTLS    bool
    81  	ACMEHosts    []string
    82  	TLSConfig    *tls.Config
    83  	Resolver     resolver.Resolver
    84  	Wrappers     []Wrapper
    85  }
    86  
    87  type Option func(*Options)
    88  
    89  type Wrapper func(h http.Handler) http.Handler
    90  
    91  func WrapHandler(w ...Wrapper) Option {
    92  	return func(o *Options) {
    93  		o.Wrappers = append(o.Wrappers, w...)
    94  	}
    95  }
    96  
    97  func EnableCORS(b bool) Option {
    98  	return func(o *Options) {
    99  		o.EnableCORS = b
   100  	}
   101  }
   102  
   103  func EnableACME(b bool) Option {
   104  	return func(o *Options) {
   105  		o.EnableACME = b
   106  	}
   107  }
   108  
   109  func ACMEHosts(hosts ...string) Option {
   110  	return func(o *Options) {
   111  		o.ACMEHosts = hosts
   112  	}
   113  }
   114  
   115  func ACMEProvider(p acme.Provider) Option {
   116  	return func(o *Options) {
   117  		o.ACMEProvider = p
   118  	}
   119  }
   120  
   121  func EnableTLS(b bool) Option {
   122  	return func(o *Options) {
   123  		o.EnableTLS = b
   124  	}
   125  }
   126  
   127  func TLSConfig(t *tls.Config) Option {
   128  	return func(o *Options) {
   129  		o.TLSConfig = t
   130  	}
   131  }
   132  
   133  func Resolver(r resolver.Resolver) Option {
   134  	return func(o *Options) {
   135  		o.Resolver = r
   136  	}
   137  }
   138  
   139  // Endpoint is a mapping between an RPC method and HTTP endpoint
   140  type Endpoint struct {
   141  	// RPC Method e.g. Greeter.Hello
   142  	Name string
   143  	// Description e.g what's this endpoint for
   144  	Description string
   145  	// API Handler e.g rpc, proxy
   146  	Handler string
   147  	// HTTP Host e.g example.com
   148  	Host []string
   149  	// HTTP Methods e.g GET, POST
   150  	Method []string
   151  	// HTTP Path e.g /greeter. Expect POSIX regex
   152  	Path []string
   153  	// Body destination
   154  	// "*" or "" - top level message value
   155  	// "string" - inner message value
   156  	Body string
   157  	// Stream flag
   158  	Stream bool
   159  }
   160  
   161  // Service represents an API service
   162  type Service struct {
   163  	// Name of service
   164  	Name string
   165  	// The endpoint for this service
   166  	Endpoint *Endpoint
   167  	// Versions of this service
   168  	Services []*registry.Service
   169  }
   170  
   171  // Encode encodes an endpoint to endpoint metadata
   172  func Encode(e *Endpoint) map[string]string {
   173  	if e == nil {
   174  		return nil
   175  	}
   176  
   177  	// endpoint map
   178  	ep := make(map[string]string)
   179  
   180  	// set vals only if they exist
   181  	set := func(k, v string) {
   182  		if len(v) == 0 {
   183  			return
   184  		}
   185  		ep[k] = v
   186  	}
   187  
   188  	set("endpoint", e.Name)
   189  	set("description", e.Description)
   190  	set("handler", e.Handler)
   191  	set("method", strings.Join(e.Method, ","))
   192  	set("path", strings.Join(e.Path, ","))
   193  	set("host", strings.Join(e.Host, ","))
   194  
   195  	return ep
   196  }
   197  
   198  // Decode decodes endpoint metadata into an endpoint
   199  func Decode(e map[string]string) *Endpoint {
   200  	if e == nil {
   201  		return nil
   202  	}
   203  
   204  	return &Endpoint{
   205  		Name:        e["endpoint"],
   206  		Description: e["description"],
   207  		Method:      slice(e["method"]),
   208  		Path:        slice(e["path"]),
   209  		Host:        slice(e["host"]),
   210  		Handler:     e["handler"],
   211  	}
   212  }
   213  
   214  // Validate validates an endpoint to guarantee it won't blow up when being served
   215  func Validate(e *Endpoint) error {
   216  	if e == nil {
   217  		return errors.New("endpoint is nil")
   218  	}
   219  
   220  	if len(e.Name) == 0 {
   221  		return errors.New("name required")
   222  	}
   223  
   224  	for _, p := range e.Path {
   225  		ps := p[0]
   226  		pe := p[len(p)-1]
   227  
   228  		if ps == '^' && pe == '$' {
   229  			_, err := regexp.CompilePOSIX(p)
   230  			if err != nil {
   231  				return err
   232  			}
   233  		} else if ps == '^' && pe != '$' {
   234  			return errors.New("invalid path")
   235  		} else if ps != '^' && pe == '$' {
   236  			return errors.New("invalid path")
   237  		}
   238  	}
   239  
   240  	if len(e.Handler) == 0 {
   241  		return errors.New("invalid handler")
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  func WithEndpoint(e *Endpoint) server.HandlerOption {
   248  	return server.EndpointMetadata(e.Name, Encode(e))
   249  }
   250  
   251  func slice(s string) []string {
   252  	var sl []string
   253  
   254  	for _, p := range strings.Split(s, ",") {
   255  		if str := strings.TrimSpace(p); len(str) > 0 {
   256  			sl = append(sl, strings.TrimSpace(p))
   257  		}
   258  	}
   259  
   260  	return sl
   261  }
   262  
   263  // RequestPayload takes a *http.Request and returns the payload
   264  // If the request is a GET the query string parameters are extracted and marshaled to JSON and the raw bytes are returned.
   265  // If the request method is a POST the request body is read and returned
   266  func RequestPayload(r *http.Request) ([]byte, error) {
   267  	var err error
   268  
   269  	// we have to decode json-rpc and proto-rpc because we suck
   270  	// well actually because there's no proxy codec right now
   271  
   272  	ct := r.Header.Get("Content-Type")
   273  	switch {
   274  	case strings.Contains(ct, "application/json-rpc"):
   275  		msg := codec.Message{
   276  			Type:   codec.Request,
   277  			Header: make(map[string]string),
   278  		}
   279  		c := jsonrpc.NewCodec(&buffer{r.Body})
   280  		if err = c.ReadHeader(&msg, codec.Request); err != nil {
   281  			return nil, err
   282  		}
   283  		var raw json.RawMessage
   284  		if err = c.ReadBody(&raw); err != nil {
   285  			return nil, err
   286  		}
   287  		return ([]byte)(raw), nil
   288  	case strings.Contains(ct, "application/proto-rpc"), strings.Contains(ct, "application/octet-stream"):
   289  		msg := codec.Message{
   290  			Type:   codec.Request,
   291  			Header: make(map[string]string),
   292  		}
   293  		c := protorpc.NewCodec(&buffer{r.Body})
   294  		if err = c.ReadHeader(&msg, codec.Request); err != nil {
   295  			return nil, err
   296  		}
   297  		var raw *bytes.Frame
   298  		if err = c.ReadBody(&raw); err != nil {
   299  			return nil, err
   300  		}
   301  		return raw.Data, nil
   302  	case strings.Contains(ct, "application/x-www-form-urlencoded"):
   303  		r.ParseForm()
   304  
   305  		// generate a new set of values from the form
   306  		vals := make(map[string]string)
   307  		for k, v := range r.Form {
   308  			vals[k] = strings.Join(v, ",")
   309  		}
   310  
   311  		// marshal
   312  		return json.Marshal(vals)
   313  	case strings.Contains(ct, "multipart/form-data"):
   314  		// 10MB buffer
   315  		if err := r.ParseMultipartForm(int64(10 << 20)); err != nil {
   316  			return nil, err
   317  		}
   318  		vals := make(map[string]interface{})
   319  		for k, v := range r.MultipartForm.Value {
   320  			vals[k] = strings.Join(v, ",")
   321  		}
   322  		for k, _ := range r.MultipartForm.File {
   323  			f, _, err := r.FormFile(k)
   324  			if err != nil {
   325  				return nil, err
   326  			}
   327  			b, err := ioutil.ReadAll(f)
   328  			if err != nil {
   329  				return nil, err
   330  			}
   331  			vals[k] = b
   332  		}
   333  		return json.Marshal(vals)
   334  		// TODO: application/grpc
   335  	}
   336  
   337  	// otherwise as per usual
   338  	ctx := r.Context()
   339  	// dont user metadata.FromContext as it mangles names
   340  	md, ok := metadata.FromContext(ctx)
   341  	if !ok {
   342  		md = make(map[string]string)
   343  	}
   344  
   345  	// allocate maximum
   346  	matches := make(map[string]interface{}, len(md))
   347  	bodydst := ""
   348  
   349  	// get fields from url path
   350  	for k, v := range md {
   351  		k = strings.ToLower(k)
   352  		// filter own keys
   353  		if strings.HasPrefix(k, "x-api-field-") {
   354  			matches[strings.TrimPrefix(k, "x-api-field-")] = v
   355  			delete(md, k)
   356  		} else if k == "x-api-body" {
   357  			bodydst = v
   358  			delete(md, k)
   359  		}
   360  	}
   361  
   362  	// map of all fields
   363  	req := make(map[string]interface{}, len(md))
   364  
   365  	// get fields from url values
   366  	if len(r.URL.RawQuery) > 0 {
   367  		umd := make(map[string]interface{})
   368  		err = qson.Unmarshal(&umd, r.URL.RawQuery)
   369  		if err != nil {
   370  			return nil, err
   371  		}
   372  		for k, v := range umd {
   373  			matches[k] = v
   374  		}
   375  	}
   376  
   377  	// restore context without fields
   378  	*r = *r.Clone(metadata.NewContext(ctx, md))
   379  
   380  	for k, v := range matches {
   381  		ps := strings.Split(k, ".")
   382  		if len(ps) == 1 {
   383  			req[k] = v
   384  			continue
   385  		}
   386  		em := make(map[string]interface{})
   387  		em[ps[len(ps)-1]] = v
   388  		for i := len(ps) - 2; i > 0; i-- {
   389  			nm := make(map[string]interface{})
   390  			nm[ps[i]] = em
   391  			em = nm
   392  		}
   393  		if vm, ok := req[ps[0]]; ok {
   394  			// nested map
   395  			nm := vm.(map[string]interface{})
   396  			for vk, vv := range em {
   397  				nm[vk] = vv
   398  			}
   399  			req[ps[0]] = nm
   400  		} else {
   401  			req[ps[0]] = em
   402  		}
   403  	}
   404  	pathbuf := []byte("{}")
   405  	if len(req) > 0 {
   406  		pathbuf, err = json.Marshal(req)
   407  		if err != nil {
   408  			return nil, err
   409  		}
   410  	}
   411  
   412  	urlbuf := []byte("{}")
   413  	out, err := jsonpatch.MergeMergePatches(urlbuf, pathbuf)
   414  	if err != nil {
   415  		return nil, err
   416  	}
   417  
   418  	switch r.Method {
   419  	case "GET":
   420  		// empty response
   421  		if strings.Contains(ct, "application/json") && string(out) == "{}" {
   422  			return out, nil
   423  		} else if string(out) == "{}" && !strings.Contains(ct, "application/json") {
   424  			return []byte{}, nil
   425  		}
   426  		return out, nil
   427  	case "PATCH", "POST", "PUT", "DELETE":
   428  		bodybuf := []byte("{}")
   429  		buf := bufferPool.Get()
   430  		defer bufferPool.Put(buf)
   431  		if _, err := buf.ReadFrom(r.Body); err != nil {
   432  			return nil, err
   433  		}
   434  		if b := buf.Bytes(); len(b) > 0 {
   435  			bodybuf = b
   436  		}
   437  		if bodydst == "" || bodydst == "*" {
   438  			// jsonpatch resequences the json object so we avoid it if possible (some usecases such as
   439  			// validating signatures require the request body to be unchangedd). We're keeping support
   440  			// for the custom paramaters for backwards compatability reasons.
   441  			if string(out) == "{}" {
   442  				return bodybuf, nil
   443  			}
   444  
   445  			if out, err = jsonpatch.MergeMergePatches(out, bodybuf); err == nil {
   446  				return out, nil
   447  			}
   448  		}
   449  		var jsonbody map[string]interface{}
   450  		if json.Valid(bodybuf) {
   451  			if err = json.Unmarshal(bodybuf, &jsonbody); err != nil {
   452  				return nil, err
   453  			}
   454  		}
   455  		dstmap := make(map[string]interface{})
   456  		ps := strings.Split(bodydst, ".")
   457  		if len(ps) == 1 {
   458  			if jsonbody != nil {
   459  				dstmap[ps[0]] = jsonbody
   460  			} else {
   461  				// old unexpected behaviour
   462  				dstmap[ps[0]] = bodybuf
   463  			}
   464  		} else {
   465  			em := make(map[string]interface{})
   466  			if jsonbody != nil {
   467  				em[ps[len(ps)-1]] = jsonbody
   468  			} else {
   469  				// old unexpected behaviour
   470  				em[ps[len(ps)-1]] = bodybuf
   471  			}
   472  			for i := len(ps) - 2; i > 0; i-- {
   473  				nm := make(map[string]interface{})
   474  				nm[ps[i]] = em
   475  				em = nm
   476  			}
   477  			dstmap[ps[0]] = em
   478  		}
   479  
   480  		bodyout, err := json.Marshal(dstmap)
   481  		if err != nil {
   482  			return nil, err
   483  		}
   484  
   485  		if out, err = jsonpatch.MergeMergePatches(out, bodyout); err == nil {
   486  			return out, nil
   487  		}
   488  
   489  		//fallback to previous unknown behaviour
   490  		return bodybuf, nil
   491  	}
   492  
   493  	return []byte{}, nil
   494  }