github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/services/handler.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package services
    19  
    20  import (
    21  	"bytes"
    22  	"github.com/aacfactory/errors"
    23  	"github.com/aacfactory/fns/commons/avros"
    24  	"github.com/aacfactory/fns/commons/bytex"
    25  	"github.com/aacfactory/fns/commons/mmhash"
    26  	"github.com/aacfactory/fns/commons/objects"
    27  	"github.com/aacfactory/fns/commons/versions"
    28  	"github.com/aacfactory/fns/context"
    29  	"github.com/aacfactory/fns/transports"
    30  	"github.com/aacfactory/json"
    31  	"github.com/valyala/bytebufferpool"
    32  	"golang.org/x/sync/singleflight"
    33  	"strconv"
    34  	"sync/atomic"
    35  )
    36  
    37  var (
    38  	slashBytes = []byte{'/'}
    39  )
    40  
    41  var (
    42  	ErrDeviceId               = errors.NotAcceptable("fns: X-Fns-Device-Id is required")
    43  	ErrInvalidPath            = errors.Warning("fns: invalid path")
    44  	ErrInvalidBody            = errors.Warning("fns: invalid body")
    45  	ErrInvalidRequestVersions = errors.Warning("fns: invalid request versions")
    46  )
    47  
    48  func Handler(endpoints Endpoints) transports.MuxHandler {
    49  	return &endpointsHandler{
    50  		endpoints: endpoints,
    51  		loaded:    atomic.Bool{},
    52  		infos:     nil,
    53  		group:     singleflight.Group{},
    54  	}
    55  }
    56  
    57  type endpointsHandler struct {
    58  	endpoints Endpoints
    59  	loaded    atomic.Bool
    60  	infos     EndpointInfos
    61  	group     singleflight.Group
    62  }
    63  
    64  func (handler *endpointsHandler) Name() string {
    65  	return "endpoints"
    66  }
    67  
    68  func (handler *endpointsHandler) Construct(_ transports.MuxHandlerOptions) error {
    69  	return nil
    70  }
    71  
    72  func (handler *endpointsHandler) Match(_ context.Context, method []byte, path []byte, header transports.Header) bool {
    73  	if !handler.loaded.Load() {
    74  		handler.infos = handler.endpoints.Info()
    75  		handler.loaded.Store(true)
    76  	}
    77  	pathItems := bytes.Split(path, slashBytes)
    78  	if len(pathItems) != 3 {
    79  		return false
    80  	}
    81  
    82  	ep := pathItems[1]
    83  	fn := pathItems[2]
    84  	endpoint, hasEndpoint := handler.infos.Find(ep)
    85  	if !hasEndpoint {
    86  		return false
    87  	}
    88  	if endpoint.Internal {
    89  		return false
    90  	}
    91  	fi, hasFn := endpoint.Functions.Find(fn)
    92  	if !hasFn {
    93  		return false
    94  	}
    95  	if fi.Internal {
    96  		return false
    97  	}
    98  	if fi.Readonly {
    99  		return bytes.Equal(method, transports.MethodGet)
   100  	}
   101  	if !bytes.Equal(method, transports.MethodPost) {
   102  		return false
   103  	}
   104  	ok := bytes.Equal(method, transports.MethodPost) &&
   105  		(bytes.Equal(header.Get(transports.ContentTypeHeaderName), transports.ContentTypeJsonHeaderValue) ||
   106  			bytes.Equal(header.Get(transports.ContentTypeHeaderName), transports.ContentTypeAvroHeaderValue))
   107  	return ok
   108  }
   109  
   110  func (handler *endpointsHandler) Handle(w transports.ResponseWriter, r transports.Request) {
   111  	groupKeyBuf := bytebufferpool.Get()
   112  
   113  	// path
   114  	path := r.Path()
   115  	pathItems := bytes.Split(path, slashBytes)
   116  	if len(pathItems) != 3 {
   117  		bytebufferpool.Put(groupKeyBuf)
   118  		w.Failed(ErrInvalidPath.WithMeta("path", bytex.ToString(path)))
   119  		return
   120  	}
   121  	ep := pathItems[1]
   122  	fn := pathItems[2]
   123  	_, _ = groupKeyBuf.Write(path)
   124  
   125  	// header >>>
   126  	options := make([]RequestOption, 0, 1)
   127  	// device id
   128  	deviceId := r.Header().Get(transports.DeviceIdHeaderName)
   129  	if len(deviceId) == 0 {
   130  		bytebufferpool.Put(groupKeyBuf)
   131  		w.Failed(ErrDeviceId.WithMeta("path", bytex.ToString(path)))
   132  		return
   133  	}
   134  	options = append(options, WithDeviceId(deviceId))
   135  	_, _ = groupKeyBuf.Write(deviceId)
   136  	// device ip
   137  	deviceIp := transports.DeviceIp(r)
   138  	if len(deviceIp) > 0 {
   139  		options = append(options, WithDeviceIp(deviceIp))
   140  	}
   141  	// request id
   142  	requestId := r.Header().Get(transports.RequestIdHeaderName)
   143  	if len(requestId) > 0 {
   144  		options = append(options, WithRequestId(requestId))
   145  	}
   146  	// request version
   147  	acceptedVersions := r.Header().Get(transports.RequestVersionsHeaderName)
   148  	if len(acceptedVersions) > 0 {
   149  		intervals, intervalsErr := versions.ParseIntervals(acceptedVersions)
   150  		if intervalsErr != nil {
   151  			bytebufferpool.Put(groupKeyBuf)
   152  			w.Failed(ErrInvalidRequestVersions.WithMeta("path", bytex.ToString(path)).WithMeta("versions", bytex.ToString(acceptedVersions)).WithCause(intervalsErr))
   153  			return
   154  		}
   155  		options = append(options, WithRequestVersions(intervals))
   156  		_, _ = groupKeyBuf.Write(acceptedVersions)
   157  	}
   158  	// authorization
   159  	authorization := r.Header().Get(transports.AuthorizationHeaderName)
   160  	if len(authorization) > 0 {
   161  		options = append(options, WithToken(authorization))
   162  		_, _ = groupKeyBuf.Write(authorization)
   163  	}
   164  
   165  	// header <<<
   166  
   167  	// param
   168  	var param objects.Object
   169  	method := r.Method()
   170  	if bytes.Equal(method, transports.MethodGet) {
   171  		// query
   172  		queryParams := r.Params()
   173  		param = transports.ObjectParams(queryParams)
   174  		_, _ = groupKeyBuf.Write(queryParams.Encode())
   175  	} else {
   176  		// body
   177  		body, bodyErr := r.Body()
   178  		if bodyErr != nil {
   179  			bytebufferpool.Put(groupKeyBuf)
   180  			w.Failed(ErrInvalidBody.WithMeta("path", bytex.ToString(path)))
   181  			return
   182  		}
   183  		contentType := r.Header().Get(transports.ContentTypeHeaderName)
   184  		if bytes.Equal(contentType, transports.ContentTypeJsonHeaderValue) {
   185  			param = json.RawMessage(body)
   186  		} else if bytes.Equal(contentType, transports.ContentTypeAvroHeaderValue) {
   187  			param = avros.RawMessage(body)
   188  		} else {
   189  			if json.Validate(body) {
   190  				param = json.RawMessage(body)
   191  			} else {
   192  				bytebufferpool.Put(groupKeyBuf)
   193  				w.Failed(ErrInvalidBody.WithMeta("path", bytex.ToString(path)))
   194  				return
   195  			}
   196  		}
   197  		_, _ = groupKeyBuf.Write(body)
   198  	}
   199  
   200  	// handle
   201  	groupKey := strconv.FormatUint(mmhash.Sum64(groupKeyBuf.Bytes()), 16)
   202  	bytebufferpool.Put(groupKeyBuf)
   203  	v, err, _ := handler.group.Do(groupKey, func() (v interface{}, err error) {
   204  		v, err = handler.endpoints.Request(
   205  			r, ep, fn,
   206  			param,
   207  			options...,
   208  		)
   209  		return
   210  	})
   211  	handler.group.Forget(groupKey)
   212  	if err != nil {
   213  		w.Failed(err)
   214  		return
   215  	}
   216  	response := v.(Response)
   217  
   218  	if response.Valid() {
   219  		w.Succeed(response.Value())
   220  	} else {
   221  		w.Succeed(nil)
   222  	}
   223  }
   224  
   225  type MuxHandler interface {
   226  	transports.MuxHandler
   227  	Services() []Service
   228  }
   229  
   230  type Middleware interface {
   231  	transports.Middleware
   232  	Services() []Service
   233  }