github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/fn.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 clusters
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"github.com/aacfactory/avro"
    24  	"github.com/aacfactory/errors"
    25  	"github.com/aacfactory/fns/commons/avros"
    26  	"github.com/aacfactory/fns/commons/signatures"
    27  	"github.com/aacfactory/fns/commons/window"
    28  	"github.com/aacfactory/fns/services"
    29  	"github.com/aacfactory/fns/services/tracings"
    30  	"github.com/aacfactory/fns/transports"
    31  	"github.com/aacfactory/fns/transports/middlewares/compress"
    32  	"github.com/aacfactory/json"
    33  	"github.com/aacfactory/logs"
    34  	"net/http"
    35  	"sync/atomic"
    36  )
    37  
    38  type Fn struct {
    39  	log          logs.Logger
    40  	address      string
    41  	endpointName string
    42  	name         string
    43  	internal     bool
    44  	readonly     bool
    45  	path         []byte
    46  	signature    signatures.Signature
    47  	errs         *window.Times
    48  	health       atomic.Bool
    49  	client       transports.Client
    50  }
    51  
    52  func (fn *Fn) Enable() bool {
    53  	return fn.health.Load()
    54  }
    55  
    56  func (fn *Fn) Name() string {
    57  	return fn.name
    58  }
    59  
    60  func (fn *Fn) Internal() bool {
    61  	return fn.internal
    62  }
    63  
    64  func (fn *Fn) Readonly() bool {
    65  	return fn.readonly
    66  }
    67  
    68  func (fn *Fn) Handle(ctx services.Request) (v interface{}, err error) {
    69  	if !ctx.Header().Internal() {
    70  		err = errors.Warning("fns: request must be internal")
    71  		return
    72  	}
    73  	// header >>>
    74  	header := transports.AcquireHeader()
    75  	defer transports.ReleaseHeader(header)
    76  	// try copy transport request header
    77  	transportRequestHeader, hasTransportRequestHeader := transports.TryLoadRequestHeader(ctx)
    78  	if hasTransportRequestHeader {
    79  		transportRequestHeader.Foreach(func(key []byte, values [][]byte) {
    80  			ok := bytes.Equal(key, transports.CookieHeaderName) ||
    81  				bytes.Equal(key, transports.XForwardedForHeaderName) ||
    82  				bytes.Equal(key, transports.OriginHeaderName) ||
    83  				bytes.Index(key, transports.UserHeaderNamePrefix) == 0
    84  			if ok {
    85  				for _, value := range values {
    86  					header.Add(key, value)
    87  				}
    88  			}
    89  		})
    90  	}
    91  	// accept
    92  	header.Set(transports.AcceptEncodingHeaderName, transports.ContentTypeAvroHeaderValue)
    93  	// content-type
    94  	header.Set(transports.ContentTypeHeaderName, internalContentTypeHeader)
    95  
    96  	// endpoint id
    97  	endpointId := ctx.Header().EndpointId()
    98  	if len(endpointId) > 0 {
    99  		header.Set(transports.EndpointIdHeaderName, endpointId)
   100  	}
   101  	// device id
   102  	deviceId := ctx.Header().DeviceId()
   103  	if len(deviceId) > 0 {
   104  		header.Set(transports.DeviceIdHeaderName, deviceId)
   105  	}
   106  	// device ip
   107  	deviceIp := ctx.Header().DeviceIp()
   108  	if len(deviceIp) > 0 {
   109  		header.Set(transports.DeviceIpHeaderName, deviceIp)
   110  	}
   111  	// request id
   112  	requestId := ctx.Header().RequestId()
   113  	if len(requestId) > 0 {
   114  		header.Set(transports.RequestIdHeaderName, requestId)
   115  	}
   116  	// request version
   117  	requestVersion := ctx.Header().AcceptedVersions()
   118  	if len(requestVersion) > 0 {
   119  		header.Set(transports.RequestVersionsHeaderName, requestVersion.Bytes())
   120  	}
   121  	// authorization
   122  	token := ctx.Header().Token()
   123  	if len(token) > 0 {
   124  		header.Set(transports.AuthorizationHeaderName, token)
   125  	}
   126  	// header <<<
   127  
   128  	// body
   129  	userValues := make([]Entry, 0, 1)
   130  	ctx.UserValues(func(key []byte, val any) {
   131  		p, encodeErr := json.Marshal(val)
   132  		if encodeErr != nil {
   133  			return
   134  		}
   135  		userValues = append(userValues, Entry{
   136  			Key:   key,
   137  			Value: p,
   138  		})
   139  	})
   140  	var argument []byte
   141  	var argumentErr error
   142  	if ctx.Param().Valid() {
   143  		param := ctx.Param().Value()
   144  		argument, argumentErr = avro.Marshal(param)
   145  	}
   146  	if argumentErr != nil {
   147  		err = errors.Warning("fns: encode request argument failed").WithCause(argumentErr).WithMeta("endpoint", fn.endpointName).WithMeta("fn", fn.name)
   148  		return
   149  	}
   150  	rb := RequestBody{
   151  		ContextUserValues: userValues,
   152  		Params:            argument,
   153  	}
   154  	body, bodyErr := avro.Marshal(rb)
   155  	if bodyErr != nil {
   156  		err = errors.Warning("fns: encode body failed").WithCause(bodyErr).WithMeta("endpoint", fn.endpointName).WithMeta("fn", fn.name)
   157  		return
   158  	}
   159  	// sign
   160  	signature := fn.signature.Sign(body)
   161  	header.Set(transports.SignatureHeaderName, signature)
   162  
   163  	// do
   164  	status, respHeader, respBody, doErr := fn.client.Do(ctx, transports.MethodPost, fn.path, header, body)
   165  	if doErr != nil {
   166  		n := fn.errs.Incr()
   167  		if n > 10 {
   168  			fn.health.Store(false)
   169  		}
   170  		err = errors.Warning("fns: internal endpoint handle failed").WithCause(doErr).WithMeta("endpoint", fn.endpointName).WithMeta("fn", fn.name)
   171  		return
   172  	}
   173  	// debug
   174  	if fn.log.DebugEnabled() {
   175  		fn.log.Debug().
   176  			With("address", fn.address).
   177  			With("endpoint", fn.endpointName).
   178  			With("fn", fn.name).
   179  			With("status", status).
   180  			Message(fmt.Sprintf("fns: status of internal endpoint is %d", status))
   181  	}
   182  
   183  	// try copy transport response header
   184  	transportResponseHeader, hasTransportResponseHeader := transports.TryLoadResponseHeader(ctx)
   185  	if hasTransportResponseHeader {
   186  		respHeader.Foreach(func(key []byte, values [][]byte) {
   187  			ok := bytes.Equal(key, transports.CookieHeaderName) ||
   188  				bytes.Index(key, transports.UserHeaderNamePrefix) == 0
   189  			if ok {
   190  				for _, value := range values {
   191  					transportResponseHeader.Add(key, value)
   192  				}
   193  			}
   194  		})
   195  	}
   196  
   197  	if status == 200 {
   198  		if fn.errs.Value() > 0 {
   199  			fn.errs.Decr()
   200  		}
   201  		respBody, err = compress.DecodeResponse(respHeader, respBody)
   202  		if err != nil {
   203  			err = errors.Warning("fns: internal endpoint handle failed").WithCause(err).WithMeta("endpoint", fn.endpointName).WithMeta("fn", fn.name)
   204  			return
   205  		}
   206  		rsb := ResponseBody{}
   207  		decodeErr := avro.Unmarshal(respBody, &rsb)
   208  		if decodeErr != nil {
   209  			err = errors.Warning("fns: internal endpoint handle failed").WithCause(decodeErr).WithMeta("endpoint", fn.endpointName).WithMeta("fn", fn.name)
   210  			return
   211  		}
   212  		trace, hasTrace := tracings.Load(ctx)
   213  		if hasTrace {
   214  			span, hasSpan := rsb.GetSpan()
   215  			if hasSpan {
   216  				trace.Mount(span)
   217  			}
   218  		}
   219  		if rsb.Succeed {
   220  			v = avros.RawMessage(rsb.Data)
   221  		} else {
   222  			codeErr := &errors.CodeErrorImpl{}
   223  			_ = avro.Unmarshal(rsb.Data, codeErr)
   224  			err = codeErr
   225  		}
   226  		return
   227  	}
   228  	switch status {
   229  	case http.StatusServiceUnavailable:
   230  		fn.health.Store(false)
   231  		err = ErrUnavailable
   232  		break
   233  	case http.StatusTooManyRequests:
   234  		err = ErrTooMayRequest
   235  		break
   236  	case http.StatusTooEarly:
   237  		err = ErrTooEarly
   238  		break
   239  	case 555:
   240  		respBody, err = compress.DecodeResponse(respHeader, respBody)
   241  		if err != nil {
   242  			err = errors.Warning("fns: internal endpoint handle failed").WithCause(err).WithMeta("endpoint", fn.endpointName).WithMeta("fn", fn.name)
   243  			return
   244  		}
   245  		codeErr := &errors.CodeErrorImpl{}
   246  		_ = avro.Unmarshal(respBody, codeErr)
   247  		err = codeErr
   248  		break
   249  	case 666:
   250  		respBody, err = compress.DecodeResponse(respHeader, respBody)
   251  		if err != nil {
   252  			err = errors.Warning("fns: internal endpoint handle failed").WithCause(err).WithMeta("endpoint", fn.endpointName).WithMeta("fn", fn.name)
   253  			return
   254  		}
   255  		err = errors.New(666, "***INTERNAL FAILED***", string(respBody))
   256  		break
   257  	}
   258  	return
   259  }