github.com/cloudwego/kitex@v0.9.0/pkg/generic/descriptor/http.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     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  package descriptor
    18  
    19  import (
    20  	"errors"
    21  	"net/http"
    22  	"net/url"
    23  
    24  	"github.com/bytedance/sonic/ast"
    25  	dhttp "github.com/cloudwego/dynamicgo/http"
    26  
    27  	"github.com/cloudwego/kitex/pkg/utils"
    28  )
    29  
    30  // MIMEType ...
    31  type MIMEType string
    32  
    33  const (
    34  	MIMEApplicationJson     = "application/json"
    35  	MIMEApplicationProtobuf = "application/x-protobuf"
    36  )
    37  
    38  var (
    39  	_ dhttp.RequestGetter  = &HTTPRequest{}
    40  	_ dhttp.ResponseSetter = &HTTPResponse{}
    41  )
    42  
    43  // HTTPRequest ...
    44  type HTTPRequest struct {
    45  	Params      *Params // path params
    46  	Request     *http.Request
    47  	RawBody     []byte
    48  	Body        map[string]interface{}
    49  	GeneralBody interface{} // body of other representation, used with ContentType
    50  	ContentType MIMEType
    51  	cookies     map[string]string
    52  	query       url.Values
    53  	bodyMap     *ast.Node
    54  }
    55  
    56  // GetHeader implements http.RequestGetter of dynamicgo
    57  func (req *HTTPRequest) GetHeader(key string) string {
    58  	return req.Request.Header.Get(key)
    59  }
    60  
    61  // GetCookie implements http.RequestGetter of dynamicgo
    62  func (req *HTTPRequest) GetCookie(key string) string {
    63  	if req.cookies == nil {
    64  		req.cookies = map[string]string{}
    65  		for _, cookie := range req.Request.Cookies() {
    66  			req.cookies[cookie.Name] = cookie.Value
    67  		}
    68  	}
    69  	return req.cookies[key]
    70  }
    71  
    72  // GetQuery implements http.RequestGetter of dynamicgo
    73  func (req *HTTPRequest) GetQuery(key string) string {
    74  	if req.Request.URL == nil {
    75  		return ""
    76  	}
    77  	if req.query == nil {
    78  		req.query = req.Request.URL.Query()
    79  	}
    80  	return req.query.Get(key)
    81  }
    82  
    83  // GetBody implements http.RequestGetter of dynamicgo
    84  func (req *HTTPRequest) GetBody() []byte {
    85  	return req.RawBody
    86  }
    87  
    88  // GetMethod implements http.RequestGetter of dynamicgo
    89  func (req *HTTPRequest) GetMethod() string {
    90  	return req.Request.Method
    91  }
    92  
    93  // GetPath implements http.RequestGetter of dynamicgo
    94  func (req *HTTPRequest) GetPath() string {
    95  	if req.Request.URL == nil {
    96  		return ""
    97  	}
    98  	return req.Request.URL.Path
    99  }
   100  
   101  // GetHost implements http.RequestGetter of dynamicgo
   102  func (req *HTTPRequest) GetHost() string {
   103  	return req.Request.Host
   104  }
   105  
   106  // GetParam implements http.RequestGetter of dynamicgo
   107  func (req *HTTPRequest) GetParam(key string) string {
   108  	return req.Params.ByName(key)
   109  }
   110  
   111  // GetMapBody implements http.RequestGetter of dynamicgo
   112  func (req *HTTPRequest) GetMapBody(key string) string {
   113  	if err := req.initializeBodyMap(); err != nil {
   114  		return ""
   115  	}
   116  
   117  	v := req.bodyMap.Get(key)
   118  	if v.Check() != nil {
   119  		return ""
   120  	}
   121  	if _, err := v.Raw(); err != nil {
   122  		return ""
   123  	}
   124  	j, err := v.String()
   125  	if err != nil {
   126  		return ""
   127  	}
   128  	return j
   129  }
   130  
   131  // GetPostForm implements http.RequestGetter of dynamicgo
   132  func (req *HTTPRequest) GetPostForm(key string) string {
   133  	return req.Request.PostFormValue(key)
   134  }
   135  
   136  // GetUri implements http.RequestGetter of dynamicgo
   137  func (req *HTTPRequest) GetUri() string {
   138  	if req.Request.URL == nil {
   139  		return ""
   140  	}
   141  	return req.Request.URL.String()
   142  }
   143  
   144  func (req *HTTPRequest) initializeBodyMap() error {
   145  	if req.bodyMap == nil {
   146  		if len(req.RawBody) == 0 {
   147  			return errors.New("the length of RawBody is 0")
   148  		}
   149  		body := req.RawBody
   150  		s := utils.SliceByteToString(body)
   151  		node := ast.NewRaw(s)
   152  		req.bodyMap = &node
   153  	}
   154  	return nil
   155  }
   156  
   157  // HTTPResponse ...
   158  type HTTPResponse struct {
   159  	Header      http.Header
   160  	StatusCode  int32
   161  	RawBody     []byte // this field is set only when generic.UseRawBodyForHTTPResp(true) is set
   162  	Body        map[string]interface{}
   163  	GeneralBody interface{} // body of other representation, used with ContentType
   164  	ContentType MIMEType
   165  	Renderer    Renderer
   166  }
   167  
   168  // NewHTTPResponse HTTP response for JSON body
   169  func NewHTTPResponse() *HTTPResponse {
   170  	return &HTTPResponse{
   171  		Header:      http.Header{},
   172  		ContentType: MIMEApplicationJson,
   173  		Body:        map[string]interface{}{},
   174  		Renderer:    JsonRenderer{},
   175  	}
   176  }
   177  
   178  // SetStatusCode implements http.ResponseSetter of dynamicgo
   179  func (resp *HTTPResponse) SetStatusCode(code int) error {
   180  	resp.StatusCode = int32(code)
   181  	return nil
   182  }
   183  
   184  // SetHeader implements http.ResponseSetter of dynamicgo
   185  func (resp *HTTPResponse) SetHeader(key, val string) error {
   186  	resp.Header.Set(key, val)
   187  	return nil
   188  }
   189  
   190  // SetCookie implements http.ResponseSetter of dynamicgo
   191  func (resp *HTTPResponse) SetCookie(key, val string) error {
   192  	// kitex generic call does not care about Cookie
   193  	return nil
   194  }
   195  
   196  // SetRawBody implements http.ResponseSetter of dynamicgo
   197  func (resp *HTTPResponse) SetRawBody(body []byte) error {
   198  	resp.RawBody = body
   199  	return nil
   200  }
   201  
   202  func NewHTTPPbResponse(initBody interface{}) *HTTPResponse {
   203  	return &HTTPResponse{
   204  		Header:      http.Header{},
   205  		ContentType: MIMEApplicationProtobuf,
   206  		GeneralBody: initBody,
   207  		Renderer:    PbRenderer{},
   208  	}
   209  }
   210  
   211  // NewGeneralHTTPResponse init response with given MIMEType and body
   212  func NewGeneralHTTPResponse(contentType MIMEType, initBody interface{}, renderer Renderer) *HTTPResponse {
   213  	return &HTTPResponse{
   214  		Header:      http.Header{},
   215  		ContentType: contentType,
   216  		GeneralBody: initBody,
   217  		Renderer:    renderer,
   218  	}
   219  }
   220  
   221  // Write to ResponseWriter
   222  func (resp *HTTPResponse) Write(w http.ResponseWriter) error {
   223  	w.WriteHeader(int(resp.StatusCode))
   224  	for k := range resp.Header {
   225  		w.Header().Set(k, resp.Header.Get(k))
   226  	}
   227  
   228  	resp.Renderer.WriteContentType(w)
   229  
   230  	if resp.Body != nil {
   231  		return resp.Renderer.Render(w, resp.Body)
   232  	}
   233  
   234  	return resp.Renderer.Render(w, resp.GeneralBody)
   235  }
   236  
   237  // Param in request path
   238  type Param struct {
   239  	Key   string
   240  	Value string
   241  }
   242  
   243  // Params and recyclable
   244  type Params struct {
   245  	params   []Param
   246  	recycle  func(*Params)
   247  	recycled bool
   248  }
   249  
   250  // Recycle the Params
   251  func (ps *Params) Recycle() {
   252  	if ps.recycled {
   253  		return
   254  	}
   255  	ps.recycled = true
   256  	ps.recycle(ps)
   257  }
   258  
   259  // ByName search Param by given name
   260  func (ps *Params) ByName(name string) string {
   261  	for _, p := range ps.params {
   262  		if p.Key == name {
   263  			return p.Value
   264  		}
   265  	}
   266  	return ""
   267  }