github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/clusters/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 clusters 19 20 import ( 21 "bytes" 22 "github.com/aacfactory/avro" 23 "github.com/aacfactory/errors" 24 "github.com/aacfactory/fns/commons/avros" 25 "github.com/aacfactory/fns/commons/bytex" 26 "github.com/aacfactory/fns/commons/signatures" 27 "github.com/aacfactory/fns/commons/versions" 28 "github.com/aacfactory/fns/context" 29 "github.com/aacfactory/fns/services" 30 "github.com/aacfactory/fns/services/tracings" 31 "github.com/aacfactory/fns/transports" 32 ) 33 34 var ( 35 slashBytes = []byte{'/'} 36 internalContentTypeHeader = []byte("application/avro+fns") 37 spanKey = []byte("span") 38 ) 39 40 type Entry struct { 41 Key []byte `json:"key" avro:"key"` 42 Value []byte `json:"value" avro:"value"` 43 } 44 45 type RequestBody struct { 46 ContextUserValues []Entry `json:"contextUserValues" avro:"contextUserValues"` 47 Params []byte `json:"params" avro:"params"` 48 } 49 50 type ResponseBody struct { 51 Succeed bool `json:"succeed" avro:"succeed"` 52 Data []byte `json:"data" avro:"data"` 53 Attachments []Entry `json:"attachments" avro:"attachments"` 54 } 55 56 func (rsp ResponseBody) GetSpan() (v *tracings.Span, has bool) { 57 for _, attachment := range rsp.Attachments { 58 if bytes.Equal(attachment.Key, spanKey) { 59 if len(attachment.Value) == 0 { 60 return 61 } 62 v = new(tracings.Span) 63 err := avro.Unmarshal(attachment.Value, v) 64 if err != nil { 65 return 66 } 67 has = true 68 return 69 } 70 } 71 return 72 } 73 74 func NewInternalHandler(local services.Endpoints, signature signatures.Signature) transports.MuxHandler { 75 return &InternalHandler{ 76 signature: signature, 77 endpoints: local, 78 } 79 } 80 81 type InternalHandler struct { 82 signature signatures.Signature 83 endpoints services.Endpoints 84 } 85 86 func (handler *InternalHandler) Name() string { 87 return "internal" 88 } 89 90 func (handler *InternalHandler) Construct(_ transports.MuxHandlerOptions) error { 91 return nil 92 } 93 94 func (handler *InternalHandler) Match(_ context.Context, method []byte, path []byte, header transports.Header) bool { 95 matched := bytes.Equal(method, transports.MethodPost) && 96 len(bytes.Split(path, slashBytes)) == 3 && 97 len(header.Get(transports.SignatureHeaderName)) != 0 && 98 bytes.Equal(header.Get(transports.ContentTypeHeaderName), internalContentTypeHeader) 99 return matched 100 } 101 102 func (handler *InternalHandler) Handle(w transports.ResponseWriter, r transports.Request) { 103 // path 104 path := r.Path() 105 pathItems := bytes.Split(path, slashBytes) 106 if len(pathItems) != 3 { 107 w.Failed(ErrInvalidPath.WithMeta("path", bytex.ToString(path))) 108 return 109 } 110 service := pathItems[1] 111 fn := pathItems[2] 112 113 // sign 114 sign := r.Header().Get(transports.SignatureHeaderName) 115 if len(sign) == 0 { 116 w.Failed(ErrSignatureLost.WithMeta("path", bytex.ToString(path))) 117 return 118 } 119 // body 120 body, bodyErr := r.Body() 121 if bodyErr != nil { 122 w.Failed(ErrInvalidBody.WithMeta("path", bytex.ToString(path))) 123 return 124 } 125 126 if !handler.signature.Verify(body, sign) { 127 w.Failed(ErrSignatureUnverified.WithMeta("path", bytex.ToString(path))) 128 return 129 } 130 131 rb := RequestBody{} 132 decodeErr := avro.Unmarshal(body, &rb) 133 if decodeErr != nil { 134 w.Failed(ErrInvalidBody.WithMeta("path", bytex.ToString(path)).WithCause(decodeErr)) 135 return 136 } 137 // user values 138 for _, userValue := range rb.ContextUserValues { 139 r.SetUserValue(userValue.Key, userValue.Value) 140 } 141 142 // header >>> 143 options := make([]services.RequestOption, 0, 1) 144 // internal 145 options = append(options, services.WithInternalRequest()) 146 // endpoint id 147 endpointId := r.Header().Get(transports.EndpointIdHeaderName) 148 if len(endpointId) > 0 { 149 options = append(options, services.WithEndpointId(endpointId)) 150 } 151 // device id 152 deviceId := r.Header().Get(transports.DeviceIdHeaderName) 153 if len(deviceId) == 0 { 154 w.Failed(ErrDeviceId.WithMeta("path", bytex.ToString(path))) 155 return 156 } 157 options = append(options, services.WithDeviceId(deviceId)) 158 // device ip 159 deviceIp := r.Header().Get(transports.DeviceIpHeaderName) 160 if len(deviceIp) > 0 { 161 options = append(options, services.WithDeviceIp(deviceIp)) 162 } 163 // request id 164 requestId := r.Header().Get(transports.RequestIdHeaderName) 165 hasRequestId := len(requestId) > 0 166 if hasRequestId { 167 options = append(options, services.WithRequestId(requestId)) 168 } 169 // request version 170 acceptedVersions := r.Header().Get(transports.RequestVersionsHeaderName) 171 if len(acceptedVersions) > 0 { 172 intervals, intervalsErr := versions.ParseIntervals(acceptedVersions) 173 if intervalsErr != nil { 174 w.Failed(ErrInvalidRequestVersions.WithMeta("path", bytex.ToString(path)).WithMeta("versions", bytex.ToString(acceptedVersions)).WithCause(intervalsErr)) 175 return 176 } 177 options = append(options, services.WithRequestVersions(intervals)) 178 } 179 // authorization 180 authorization := r.Header().Get(transports.AuthorizationHeaderName) 181 if len(authorization) > 0 { 182 options = append(options, services.WithToken(authorization)) 183 } 184 // header <<< 185 186 // param 187 param := avros.RawMessage(rb.Params) 188 189 var ctx context.Context = r 190 191 // handle 192 response, err := handler.endpoints.Request( 193 ctx, service, fn, 194 param, 195 options..., 196 ) 197 succeed := err == nil 198 var data []byte 199 var dataErr error 200 var span *tracings.Span 201 if succeed { 202 if response.Valid() { 203 responseValue := response.Value() 204 data, dataErr = avro.Marshal(responseValue) 205 } 206 } else { 207 data, _ = avro.Marshal(errors.Wrap(err)) 208 } 209 if dataErr != nil { 210 succeed = false 211 data, _ = avro.Marshal(errors.Warning("fns: encode endpoint response failed").WithMeta("path", bytex.ToString(path)).WithCause(dataErr)) 212 } 213 214 if hasRequestId { 215 trace, hasTrace := tracings.Load(ctx) 216 if hasTrace { 217 span = trace.Span 218 } 219 } 220 221 rsb := ResponseBody{ 222 Succeed: succeed, 223 Data: data, 224 Attachments: make([]Entry, 0, 1), 225 } 226 if span != nil { 227 spanBytes, _ := avro.Marshal(span) 228 rsb.Attachments = append(rsb.Attachments, Entry{ 229 Key: spanKey, 230 Value: spanBytes, 231 }) 232 } 233 234 p, encodeErr := avro.Marshal(rsb) 235 if encodeErr != nil { 236 w.Failed(errors.Warning("fns: proto marshal failed").WithCause(encodeErr)) 237 return 238 } 239 _, _ = w.Write(p) 240 }