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  }