github.com/polarismesh/polaris@v1.17.8/apiserver/httpserver/utils/handler.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package utils 19 20 import ( 21 "archive/zip" 22 "bytes" 23 "context" 24 "encoding/json" 25 "errors" 26 "fmt" 27 "io" 28 "net/http" 29 "path" 30 "strings" 31 32 restful "github.com/emicklei/go-restful/v3" 33 "github.com/golang/protobuf/jsonpb" 34 "github.com/golang/protobuf/proto" 35 "github.com/golang/protobuf/ptypes/wrappers" 36 apiconfig "github.com/polarismesh/specification/source/go/api/v1/config_manage" 37 apimodel "github.com/polarismesh/specification/source/go/api/v1/model" 38 "go.uber.org/zap" 39 40 "github.com/polarismesh/polaris/apiserver/httpserver/i18n" 41 api "github.com/polarismesh/polaris/common/api/v1" 42 commonlog "github.com/polarismesh/polaris/common/log" 43 "github.com/polarismesh/polaris/common/utils" 44 ) 45 46 var ( 47 accesslog = commonlog.GetScopeOrDefaultByName(commonlog.APIServerLoggerName) 48 ) 49 50 // Handler HTTP请求/回复处理器 51 type Handler struct { 52 Request *restful.Request 53 Response *restful.Response 54 } 55 56 // ParseArray 解析PB数组对象 57 func (h *Handler) ParseArray(createMessage func() proto.Message) (context.Context, error) { 58 jsonDecoder := json.NewDecoder(h.Request.Request.Body) 59 return h.parseArray(createMessage, jsonDecoder) 60 } 61 62 // ParseArrayByText 通过字符串解析PB数组对象 63 func (h *Handler) ParseArrayByText(createMessage func() proto.Message, text string) (context.Context, error) { 64 jsonDecoder := json.NewDecoder(bytes.NewBuffer([]byte(text))) 65 return h.parseArray(createMessage, jsonDecoder) 66 } 67 68 func (h *Handler) parseArray(createMessage func() proto.Message, jsonDecoder *json.Decoder) (context.Context, error) { 69 requestID := h.Request.HeaderParameter("Request-Id") 70 // read open bracket 71 _, err := jsonDecoder.Token() 72 if err != nil { 73 accesslog.Error(err.Error(), utils.ZapRequestID(requestID)) 74 return nil, err 75 } 76 for jsonDecoder.More() { 77 protoMessage := createMessage() 78 err := jsonpb.UnmarshalNext(jsonDecoder, protoMessage) 79 if err != nil { 80 accesslog.Error(err.Error(), utils.ZapRequestID(requestID)) 81 return nil, err 82 } 83 } 84 return h.postParseMessage(requestID) 85 } 86 87 func (h *Handler) postParseMessage(requestID string) (context.Context, error) { 88 platformID := h.Request.HeaderParameter("Platform-Id") 89 platformToken := h.Request.HeaderParameter("Platform-Token") 90 token := h.Request.HeaderParameter("Polaris-Token") 91 authToken := h.Request.HeaderParameter(utils.HeaderAuthTokenKey) 92 ctx := context.Background() 93 ctx = context.WithValue(ctx, utils.StringContext("request-id"), requestID) 94 ctx = context.WithValue(ctx, utils.StringContext("platform-id"), platformID) 95 ctx = context.WithValue(ctx, utils.StringContext("platform-token"), platformToken) 96 if token != "" { 97 ctx = context.WithValue(ctx, utils.StringContext("polaris-token"), token) 98 } 99 if authToken != "" { 100 ctx = context.WithValue(ctx, utils.ContextAuthTokenKey, authToken) 101 } 102 103 var operator string 104 addrSlice := strings.Split(h.Request.Request.RemoteAddr, ":") 105 if len(addrSlice) == 2 { 106 operator = "HTTP:" + addrSlice[0] 107 if platformID != "" { 108 operator += "(" + platformID + ")" 109 } 110 } 111 if staffName := h.Request.HeaderParameter("Staffname"); staffName != "" { 112 operator = staffName 113 } 114 ctx = context.WithValue(ctx, utils.StringContext("operator"), operator) 115 116 return ctx, nil 117 } 118 119 // Parse 解析请求 120 func (h *Handler) Parse(message proto.Message) (context.Context, error) { 121 requestID := h.Request.HeaderParameter("Request-Id") 122 if err := jsonpb.Unmarshal(h.Request.Request.Body, message); err != nil { 123 accesslog.Error(err.Error(), utils.ZapRequestID(requestID)) 124 return nil, err 125 } 126 return h.postParseMessage(requestID) 127 } 128 129 // ParseHeaderContext 将http请求header中携带的用户信息提取出来 130 func (h *Handler) ParseHeaderContext() context.Context { 131 requestID := h.Request.HeaderParameter("Request-Id") 132 platformID := h.Request.HeaderParameter("Platform-Id") 133 platformToken := h.Request.HeaderParameter("Platform-Token") 134 token := h.Request.HeaderParameter("Polaris-Token") 135 authToken := h.Request.HeaderParameter(utils.HeaderAuthTokenKey) 136 137 ctx := context.Background() 138 ctx = context.WithValue(ctx, utils.StringContext("request-id"), requestID) 139 ctx = context.WithValue(ctx, utils.StringContext("platform-id"), platformID) 140 ctx = context.WithValue(ctx, utils.StringContext("platform-token"), platformToken) 141 ctx = context.WithValue(ctx, utils.ContextClientAddress, h.Request.Request.RemoteAddr) 142 if token != "" { 143 ctx = context.WithValue(ctx, utils.StringContext("polaris-token"), token) 144 } 145 if authToken != "" { 146 ctx = context.WithValue(ctx, utils.ContextAuthTokenKey, authToken) 147 } 148 149 var operator string 150 addrSlice := strings.Split(h.Request.Request.RemoteAddr, ":") 151 if len(addrSlice) == 2 { 152 operator = "HTTP:" + addrSlice[0] 153 if platformID != "" { 154 operator += "(" + platformID + ")" 155 } 156 } 157 if staffName := h.Request.HeaderParameter("Staffname"); staffName != "" { 158 operator = staffName 159 } 160 ctx = context.WithValue(ctx, utils.StringContext("operator"), operator) 161 162 return ctx 163 } 164 165 // ParseFile 解析上传的配置文件 166 func (h *Handler) ParseFile() ([]*apiconfig.ConfigFile, error) { 167 requestID := h.Request.HeaderParameter("Request-Id") 168 h.Request.Request.Body = http.MaxBytesReader(h.Response, h.Request.Request.Body, utils.MaxRequestBodySize) 169 170 file, fileHeader, err := h.Request.Request.FormFile(utils.ConfigFileFormKey) 171 if err != nil { 172 accesslog.Error(err.Error(), utils.ZapRequestID(requestID)) 173 return nil, err 174 } 175 defer file.Close() 176 177 accesslog.Info("[Config][Handler] parse upload file.", 178 zap.String("filename", fileHeader.Filename), 179 zap.Int64("filesize", fileHeader.Size), 180 zap.String("fileheader", fmt.Sprintf("%v", fileHeader.Header)), 181 ) 182 var buf bytes.Buffer 183 if _, err := io.Copy(&buf, file); err != nil { 184 accesslog.Error(err.Error(), utils.ZapRequestID(requestID)) 185 return nil, err 186 } 187 filename := fileHeader.Filename 188 contentType := http.DetectContentType(buf.Bytes()) 189 190 if contentType == "application/zip" && strings.HasSuffix(filename, ".zip") { 191 return getConfigFilesFromZIP(buf.Bytes()) 192 } 193 accesslog.Error("invalid content type", 194 utils.ZapRequestID(requestID), 195 zap.String("content-type", contentType), 196 zap.String("filename", filename), 197 ) 198 return nil, errors.New("invalid content type") 199 200 } 201 202 func getConfigFilesFromZIP(data []byte) ([]*apiconfig.ConfigFile, error) { 203 zr, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) 204 if err != nil { 205 return nil, err 206 } 207 extractFileContent := func(f *zip.File) ([]byte, error) { 208 rc, err := f.Open() 209 if err != nil { 210 accesslog.Error(err.Error(), zap.String("filename", f.Name)) 211 return nil, err 212 } 213 defer rc.Close() 214 var buf bytes.Buffer 215 if _, err := io.Copy(&buf, rc); err != nil { 216 accesslog.Error(err.Error(), zap.String("filename", f.Name)) 217 return nil, err 218 } 219 return buf.Bytes(), nil 220 } 221 var ( 222 configFiles []*apiconfig.ConfigFile 223 metas map[string]*utils.ConfigFileMeta 224 ) 225 // 提取元数据文件 226 for _, file := range zr.File { 227 if file.Name == utils.ConfigFileMetaFileName { 228 content, err := extractFileContent(file) 229 if err != nil { 230 return nil, err 231 } 232 if err := json.Unmarshal(content, &metas); err != nil { 233 accesslog.Error(err.Error(), zap.String("filename", file.Name)) 234 return nil, err 235 } 236 break 237 } 238 } 239 // 提取配置文件 240 for _, file := range zr.File { 241 // 跳过目录文件和元数据文件 242 if file.FileInfo().IsDir() || file.Name == utils.ConfigFileMetaFileName { 243 continue 244 } 245 // 提取文件内容 246 content, err := extractFileContent(file) 247 if err != nil { 248 return nil, err 249 } 250 // 解析文件组和文件名 251 var ( 252 group string 253 name string 254 ) 255 tokens := strings.SplitN(file.Name, "/", 2) 256 switch len(tokens) { 257 case 2: 258 group = tokens[0] 259 name = tokens[1] 260 case 1: 261 name = tokens[0] 262 default: 263 accesslog.Error("invalid config file", zap.String("filename", file.Name)) 264 return nil, errors.New("invalid config file") 265 } 266 267 // 解析文件扩展名 268 format := path.Ext(file.Name) 269 if format == "" { 270 format = utils.FileFormatText 271 } else { 272 format = format[1:] 273 } 274 cf := &apiconfig.ConfigFile{ 275 Group: utils.NewStringValue(group), 276 Name: utils.NewStringValue(name), 277 Content: utils.NewStringValue(string(content)), 278 Format: utils.NewStringValue(format), 279 } 280 if meta, ok := metas[file.Name]; ok { 281 if meta.Comment != "" { 282 cf.Comment = utils.NewStringValue(meta.Comment) 283 } 284 for k, v := range meta.Tags { 285 cf.Tags = append(cf.Tags, &apiconfig.ConfigFileTag{ 286 Key: utils.NewStringValue(k), 287 Value: utils.NewStringValue(v), 288 }) 289 } 290 } 291 configFiles = append(configFiles, cf) 292 } 293 return configFiles, nil 294 } 295 296 // WriteHeader 仅返回Code 297 func (h *Handler) WriteHeader(polarisCode uint32, httpStatus int) { 298 requestID := h.Request.HeaderParameter(utils.PolarisRequestID) 299 h.Request.SetAttribute(utils.PolarisCode, polarisCode) // api统计的时候,用该code 300 301 // 对于非200000的返回,补充实际的code到header中 302 if polarisCode != api.ExecuteSuccess { 303 h.Response.AddHeader(utils.PolarisCode, fmt.Sprintf("%d", polarisCode)) 304 h.Response.AddHeader(utils.PolarisMessage, api.Code2Info(polarisCode)) 305 } 306 h.Response.AddHeader("Request-Id", requestID) 307 h.Response.WriteHeader(httpStatus) 308 } 309 310 // WriteHeaderAndProto 返回Code和Proto 311 func (h *Handler) WriteHeaderAndProto(obj api.ResponseMessage) { 312 requestID := h.Request.HeaderParameter(utils.PolarisRequestID) 313 h.Request.SetAttribute(utils.PolarisCode, obj.GetCode().GetValue()) 314 status := api.CalcCode(obj) 315 316 if status != http.StatusOK { 317 accesslog.Error(obj.String(), utils.ZapRequestID(requestID)) 318 } 319 if code := obj.GetCode().GetValue(); code != api.ExecuteSuccess { 320 h.Response.AddHeader(utils.PolarisCode, fmt.Sprintf("%d", code)) 321 h.Response.AddHeader(utils.PolarisMessage, api.Code2Info(code)) 322 } 323 h.Response.AddHeader(utils.PolarisRequestID, requestID) 324 h.Response.WriteHeader(status) 325 326 m := jsonpb.Marshaler{Indent: " ", EmitDefaults: true} 327 err := m.Marshal(h.Response, h.i18nAction(obj)) 328 if err != nil { 329 accesslog.Error(err.Error(), utils.ZapRequestID(requestID)) 330 } 331 } 332 333 // WriteHeaderAndProtoV2 返回Code和Proto 334 func (h *Handler) WriteHeaderAndProtoV2(obj api.ResponseMessage) { 335 requestID := h.Request.HeaderParameter(utils.PolarisRequestID) 336 h.Request.SetAttribute(utils.PolarisCode, obj.GetCode()) 337 status := api.CalcCode(obj) 338 339 if status != http.StatusOK { 340 accesslog.Error(obj.String(), utils.ZapRequestID(requestID)) 341 } 342 if code := obj.GetCode().GetValue(); code != api.ExecuteSuccess { 343 h.Response.AddHeader(utils.PolarisCode, fmt.Sprintf("%d", code)) 344 h.Response.AddHeader(utils.PolarisMessage, api.Code2Info(code)) 345 } 346 347 h.Response.AddHeader(utils.PolarisRequestID, requestID) 348 h.Response.WriteHeader(status) 349 350 m := jsonpb.Marshaler{Indent: " ", EmitDefaults: true} 351 err := m.Marshal(h.Response, obj) 352 if err != nil { 353 accesslog.Error(err.Error(), utils.ZapRequestID(requestID)) 354 } 355 } 356 357 // HTTPResponse http答复简单封装 358 func HTTPResponse(req *restful.Request, rsp *restful.Response, code uint32) { 359 handler := &Handler{ 360 Request: req, 361 Response: rsp, 362 } 363 resp := api.NewResponse(apimodel.Code(code)) 364 handler.WriteHeaderAndProto(resp) 365 } 366 367 // i18nAction 依据resp.code进行国际化resp.info信息 368 // 当与header中的信息不匹配时, 则使用原文, 后续通过新定义code的方式增量解决 369 // 当header的msg 与 resp.info一致时, 根据resp.code国际化信息 370 func (h *Handler) i18nAction(obj api.ResponseMessage) api.ResponseMessage { 371 hMsg := h.Response.Header().Get(utils.PolarisMessage) 372 info := obj.GetInfo() 373 if hMsg != info.GetValue() { 374 return obj 375 } 376 code := obj.GetCode() 377 msg, err := i18n.Translate( 378 code.GetValue(), h.Request.QueryParameter("lang"), h.Request.HeaderParameter("Accept-Language")) 379 if msg == "" || err != nil { 380 return obj 381 } 382 *info = wrappers.StringValue{Value: msg} 383 return obj 384 } 385 386 // ParseQueryParams 解析并获取HTTP的query params 387 func ParseQueryParams(req *restful.Request) map[string]string { 388 queryParams := make(map[string]string) 389 for key, value := range req.Request.URL.Query() { 390 if len(value) > 0 { 391 queryParams[key] = value[0] // 暂时默认只支持一个查询 392 } 393 } 394 395 return queryParams 396 } 397 398 // ParseJsonBody parse http body as json object 399 func ParseJsonBody(req *restful.Request, value interface{}) error { 400 body, err := io.ReadAll(req.Request.Body) 401 if err != nil { 402 return err 403 } 404 if err := json.Unmarshal(body, value); err != nil { 405 return err 406 } 407 return nil 408 }