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 }