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 }